Який сенс у факультативному класі Гуави


89

Нещодавно я читав про це і бачив людей, які користуються цим класом, але майже у всіх випадках використання nullтакож спрацювало б, якби не інтуїтивніше. Хтось може навести конкретний приклад, коли можна Optionalбуло б досягти чогось, чого nullне вдалося чи набагато чистішим чином? Єдине, що я можу придумати, це використовувати його з Mapsнеприйнятими nullключами, але навіть це можна зробити за допомогою бічного "відображення" значення null. Хтось може надати мені переконливіший аргумент? Дякую.


12
випадковий вигук: я ненавиджу, коли люди зловживають "зразком" і роблять код таким потворним для якоїсь теоретичної вигоди, якої не існує ...
RAY

Що стосується Java8, то я більше не використовував би цей клас Guava, оскільки він менш потужний, ніж JDK. Дивіться stackoverflow.com/a/10756992/82609
Себастьян Лорбер,

Відповіді:


155

Член команди гуави тут.

Ймовірно, найбільшим недоліком nullє те, що не очевидно, що це повинно означати в будь-якому даному контексті: він не має ілюстративної назви. Не завжди очевидно, що nullозначає "немає значення для цього параметра" - чорт, як повернене значення, іноді це означає "помилка", або навіть "успіх" (!!), або просто "правильна відповідь - нічого". Optionalчасто є поняттям, яке ви насправді маєте на увазі, коли ви робите змінну нульовою, але не завжди. Якщо це не так, ми рекомендуємо написати власний клас, подібний до, Optionalале з іншою схемою іменування, щоб чітко пояснити, що ви насправді маєте на увазі.

Але я б сказав, що найбільша перевага Optionalполягає не в читабельності: перевага полягає в його ідіотостійкості. Це змушує вас активно думати про відсутність справи, якщо ви хочете, щоб ваша програма взагалі була скомпільована, оскільки вам доведеться активно розгортати Optionalта розглядати цю справу. Null дозволяє тривожно легко просто забути речі, і хоча FindBugs допомагає, я не думаю, що це вирішує проблему майже так само добре. Це особливо актуально, коли ви повертаєте значення, які можуть бути або не бути "наявними". Ви (та інші) набагато частіше забуваєте, що other.method(a, b)може повернути nullзначення, ніж ви, швидше за все, забудете, що aможе бути, nullколи ви впроваджуєте other.method. ПовертаючисьOptional робить неможливим абонентам забути цей випадок, оскільки їм доводиться розгортати об’єкт самостійно.

З цих причин ми рекомендуємо використовувати Optionalтип повернення для своїх методів, але не обов’язково в аргументах методу.

(Це, до речі, повністю вигадано з обговорення тут ).


3
+1 за чудове пояснення. Я не знаю, чи це натхнення, але я б додав покажчик на типи опцій у SML, OCaml та F #, які мають багато однакової семантики.
Адам Міхалцин,

2
Завжди здорово отримати відповідь від когось, хто брав безпосередню участь. Я б поставив +1 лише для цього. Ще +1 для чудової відповіді. (але я не можу.) Я вважаю, що суть у тому, щоб мати це як повернене значення, щоб змусити споживачів незнайомого методу докласти зусиль, щоб визнати, що "нічого" не можна повернути, є однією з переконливих причин. Мені не подобається, як цим зловживають (або навіть зловживають). Наприклад, мені дуже неприємно, коли я бачу, як люди ставлять Optionals як цінності на Картах. Відновлення карти нульовим для неіснуючого / некартованого ключа - це усталена парадигма ... Не потрібно ускладнювати її ...
RAY

9
Встановлено, що Mapповертається, nullякщо ключ не зіставлений, але пам’ятайте, що якщо ви це зробите map.put(key, null), то map.containsKey(key)повернеться, trueале map.get(key)повернеться null. Optionalможе бути корисним для прояснення різниці між випадком "явно зіставлене до нуля" та випадком "відсутній на карті". Я погоджуюсь, що цим Optionalможна зловживати, але я ще не впевнений, що ви описуєте випадок.
Луїс Вассерман

8
Щоб використати застарілу аналогію, раніше існували ці гігантські речі, які називались "телефонні книги" :-) і якщо б я попросив вас шукати чийсь номер, а ви сказали "для цієї людини немає номера", я б сказав "що ви мається на увазі, вони там з номером, який не входить до списку, або ви просто не можете знайти для них запис? " Ці дві можливі відповіді добре відображаються на Optional.absent () та null відповідно. Optional.absent () - остаточний "позитивний негатив".
Кевін Буррілліон,

3
@RAY: Певним чином, Optional<T> це значення "знайдено, але не дійсне". Або точніше, Optional<T>це спосіб прикрасити будь-який тип Tдодатковим значенням "знайдено, але не дійсне" - створення нового типу шляхом поєднання двох існуючих типів. Якщо у вас є сотня класів, може виникнути сум'яття, коли доведеться створювати окреме значення "знайдено, але не дійсне" для кожного, але Optional<T>може легко працювати для всіх них.
Даніель Прайден,

9

Це справді схоже на зразок MaybeMonad від Haskell.

Вам слід прочитати наступне, Wikipedia Monad (функціональне програмування) :

І прочитати З необов'язковою до Монада з гуава на Блозі Kerflyn в, який обговорюється про опціонної гуави , використовуваних в якості монади:


Редагувати: У Java8 є вбудований необов’язковий, який має монадичні оператори, подібні flatMap. Це була суперечлива тема, але нарешті була реалізована.

Див. Http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

public Optional<String> tryFindSimilar(String s)  //...

Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar =       opt.flatMap(this::tryFindSimilar);

flatMapОператор має важливе значення для забезпечення Монадический операцій і дозволів легко ланцюгів викликів , що всі повернуті Необов'язкові результати.

Подумайте, якщо б ви використовували mapоператор 5 разів, у підсумку ви отримали б Optional<Optional<Optional<Optional<Optional<String>>>>>, а використання flatMapдало б вамOptional<String>

Оскільки Java8, я б не хотів використовувати Guava's Optional, який є менш потужним.


6

Однією з вагомих причин його використання є те, що це робить ваші нулі дуже значущими. Замість того, щоб повертати нуль, що може означати багато речей (наприклад, помилку, помилку, порожнє тощо), ви можете вказати "ім'я" для своєї нулі. Подивіться на цей приклад:

давайте визначимо базовий POJO:

class PersonDetails {

String person;
String comments;

public PersonDetails(String person, String comments) {
    this.person = person;
    this.comments = comments;
}

public String getPerson() {
    return person;
}


public String getComments() {
    return comments;
}

}

Тепер давайте використаємо цей простий POJO:

public Optional<PersonDetails> getPersonDetailstWithOptional () {

  PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless,
  lets make the return value more meaningful*/


    if (details == null) {
      //return an absent here, caller can check for absent to signify details are not present
        return Optional.absent();
    } else {
      //else return the details wrapped in a guava 'optional'
        return Optional.of(details);   
    }
}

Тепер давайте уникати використання null і робити наші перевірки з необов’язковим, щоб це було значущим

public void checkUsingOptional () {

    Optional<PersonDetails> details = getPersonDetailstWithOptional();

    /*below condition checks if persons details are present (notice we dont check if person details are null,
    we use something more meaningful. Guava optional forces this with the implementation)*/
    if (details.isPresent()) {

      PersonDetails details = details.get();

        // proceed with further processing
        logger.info(details);

    } else {
        // do nothing
        logger.info("object was null"); 
    }

    assertFalse(details.isPresent());
}

таким чином, врешті-решт, це спосіб зробити нулі значущими та зменшити двозначність.


4

Найважливіша перевага Optional полягає в тому, що він додає більше деталей до контракту між реалізатором та абонентом функції. З цієї причини корисний як для параметрів, так і для типу повернення.

Якщо ви зробите так, щоб угода завжди мала Optionalдля можливих нульових об'єктів, ви додасте більше пояснень до таких випадків, як:

  1. Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)

    Контракт тут чітко вказує , що існує ймовірність того, що результат не повертається , а й показує , що він буде працювати з fromі toвідсутнім.

  2. Optional<Integer> maxPrime(Optional<Integer> from, Integer to)

    У контракті зазначено, що параметр from є необов’язковим, тому відсутнє значення може мати особливе значення, наприклад, починати з 2. Я можу очікувати, що нульове значення toпараметра призведе до винятку.

Отже, хороша частина використання необов’язкового полягає в тому, що контракт став одночасно описовим (подібним до @NotNull анотації), але також формальним, оскільки ви повинні написати код, .get()щоб впоратися з ним Optional.


Навіщо використовувати необов’язковий <List>, коли порожній список буде чистішим?
RAY

Я обрав неправильний приклад при поверненні. За допомогою колекцій ви можете зробити так, щоб замість нульового значення завжди поверталася порожня колекція. Якщо використовується ця умова, вам не потрібні додаткові для колекцій. Необов’язково може сприйматись як колекція з нулем або одним елементом, тому не потрібно розміщувати її навколо іншої колекції.
risercostin

2
залежно від контексту може існувати суттєва різниця між списком з 0 елементами та відсутнім списком.
plasma147
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.