В чем смысл необязательного класса Guava


89

Я недавно читал об этом и видел людей, использующих этот класс, но почти во всех случаях использование nullтакже сработало бы, если не более интуитивно. Может ли кто-нибудь привести конкретный пример, где можно Optionalбыло бы достичь чего-то, чего nullне удалось бы, или более простым способом? Единственное, что я могу придумать, - это использовать его с Mapsне принимаемыми nullключами, но даже это можно сделать с помощью бокового «сопоставления» нулевого значения. Может ли кто-нибудь предоставить мне более убедительный аргумент? Спасибо.


12
случайная напыщенная речь: я ненавижу, когда люди злоупотребляют «шаблоном» и делают код таким уродливым из-за некоторой теоретической выгоды, которой не существует ...
РЭЙ

Что касается Java8, я бы больше не использовал этот класс Guava, потому что он менее мощный, чем JDK. См. Stackoverflow.com/a/10756992/82609
Себастьян Лорбер,

Ответы:


155

Член команды Guava здесь.

Вероятно, самым большим недостатком nullявляется то, что неясно, что он должен означать в любом данном контексте: у него нет иллюстративного названия. Не всегда очевидно, что это nullозначает «нет значения для этого параметра» - черт возьми, в качестве возвращаемого значения иногда это означает «ошибку», или даже «успех» (!!), или просто «правильный ответ - ничего». Optionalчасто это понятие, которое вы имеете в виду, когда делаете переменную допускающей значение NULL, но не всегда. Если это не так, мы рекомендуем вам написать свой собственный класс, похожий на, Optionalно с другой схемой именования, чтобы прояснить, что вы на самом деле имеете в виду.

Но я бы сказал, что самое большое преимущество Optionalне в удобочитаемости: преимущество в его защищенности от идиотов. Это заставляет вас активно думать об отсутствующем случае, если вы хотите, чтобы ваша программа вообще компилировалась, поскольку вам нужно активно развернуть Optionalи обработать этот случай. Null позволяет легко забыть о чем-то, и хотя FindBugs помогает, я не думаю, что он решает проблему почти так же. Это особенно актуально, когда вы возвращаете значения, которые могут или не могут быть «присутствующими». У вас (и других) гораздо больше шансов забыть, что other.method(a, b)может возвращать nullзначение, чем вы, вероятно, забудете, что aмогло бы быть nullпри реализации other.method. ВозвращениеOptional делает невозможным для вызывающих забыть этот случай, поскольку они должны сами развернуть объект.

По этим причинам мы рекомендуем использовать Optionalв качестве возвращаемого типа для ваших методов, но не обязательно в аргументах вашего метода.

(Это, кстати, полностью взято из обсуждения здесь .)


3
+1 за отличное объяснение. Я не знаю, является ли это источником вдохновения, но я бы добавил указатель на типы параметров в SML, OCaml и F #, которые имеют во многом схожую семантику.
Адам Михалчин 05

2
Всегда приятно получить ответ от того, кто был непосредственно вовлечен. Я бы поставил +1 только за это. Хотел бы еще +1 за отличный ответ. (но я не могу.) Я думаю, что смысл использования его в качестве возвращаемого значения, чтобы заставить потребителей незнакомого метода приложить усилия, чтобы признать, что «ничего» может быть возвращено, является одной из веских причин. Мне не нравится, как им злоупотребляют (или даже злоупотребляют). например, я чувствую себя очень неловко, когда вижу, что люди помещают Optionals в качестве значений в Maps. Сохранение нулевого значения карты для несуществующего / несуществующего ключа - это хорошо устоявшаяся парадигма ... Нет необходимости усложнять ее ...
РЭЙ

9
Хорошо известно, что Mapвозвращается, nullесли ключ не сопоставлен, но помните, что если вы это сделаете map.put(key, null), то map.containsKey(key)вернется, trueно map.get(key)вернется null. Optionalможет быть удобно при прояснении различия между случаем «явно сопоставлено с нулевым значением» и случаем «не присутствует в сопоставлении». Я Optionalдопускаю, что этим можно злоупотреблять, но я еще не уверен, что описанный вами случай является злоупотреблением.
Луи Вассерман

8
Если использовать устаревшую аналогию, раньше были такие гигантские штуки, которые назывались «телефонными книгами» :-), и если бы я попросил вас найти чей-то номер, а вы сказали бы «нет номера для этого человека», я бы сказал: «Что вы делаете? значит, они там с неуказанным номером или вы просто не смогли найти для них запись? " Эти два возможных ответа хорошо отображаются на Optional.absent () и null соответственно. Optional.absent () - это окончательный «положительный отрицательный».
Кевин Бурриллион 06

3
@RAY: В некотором смысле, Optional<T> это что «найдено, но не действует» значение. Или, точнее, Optional<T>это способ украсить любой тип Tдополнительным значением «найдено, но недействительно» - создать новый тип путем объединения двух существующих типов. Если у вас есть сотня классов, то создание отдельного значения «найдено, но недействительно» для каждого Optional<T>из них может оказаться беспорядочным, но это может легко работать для всех из них.
Дэниел Прайден 07

9

Это действительно похоже на Maybeшаблон Monad из Haskell.

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

И прочитать Из необязательной к Монадам с гуавой на Блоге Kerflyn в, который обсуждается об опционной гуаве , используемых в качестве монады:


Изменить: в Java8 есть встроенный Optional, который имеет монадические операторы, такие как 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 Optional, который менее мощный.


6

Одна из веских причин для его использования заключается в том, что он делает ваши нули очень значимыми. Вместо того, чтобы возвращать значение NULL, которое может означать множество вещей (например, ошибку, сбой, пустое значение и т. Д.), Вы можете присвоить значение NULL «имя». Взгляните на этот пример:

давайте определим базовый 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 и сделаем наши проверки с помощью Optional, чтобы он имел смысл

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параметра вызовет исключение.

Таким образом, хорошая часть использования Optional заключается в том, что контракт стал как описательным (аналогично @NotNullаннотации), так и формальным, поскольку вы должны написать код, .get()чтобы справиться с ним Optional.


Зачем использовать дополнительный <List>, если пустой список будет чище?
RAY

Я выбрал неправильный пример на возврате. С коллекциями вы можете сделать соглашение, чтобы всегда возвращать пустую коллекцию вместо нулевого значения. Если используется это соглашение, для коллекций необязательно. Необязательно может восприниматься как коллекция с нулем или одним элементом, поэтому нет необходимости размещать ее вокруг другой коллекции.
raisercostin

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