Какой смысл в факультативном классе Гуавы
Я недавно читал об этом и видел людей, использующих этот класс, но в большинстве случаев, используя null
работал бы так же хорошо - если бы не более интуитивно. Может кто-то привести конкретный пример, где Optional
достигнет чего-то, что null
не мог или намного чище? Единственное, о чем я могу думать, это использовать его с Maps
которые не принимают null
ключи, но даже это можно сделать с помощью бокового "отображения" значения null. Может ли кто-нибудь предоставить мне более убедительный аргумент? Спасибо.
4 ответа
Член команды гуавы здесь.
Вероятно, самый большой недостаток null
в том, что не очевидно, что это должно означать в любом данном контексте: у него нет иллюстративного имени. Не всегда очевидно, что null
означает "нет значения для этого параметра" - черт возьми, в качестве возвращаемого значения иногда это означает "ошибка", или даже "успех" (!!), или просто "правильный ответ - ничто". Optional
это часто понятие, которое вы на самом деле имеете в виду, когда делаете переменную, допускающую значение nullable, но не всегда Если это не так, мы рекомендуем вам написать свой собственный класс, похожий на Optional
но с другой схемой именования, чтобы понять, что вы на самом деле имеете в виду.
Но я бы сказал, самое большое преимущество Optional
не в удобочитаемости: преимущество в его идиотостойкости. Это заставляет вас активно думать об отсутствующем случае, если вы хотите, чтобы ваша программа вообще компилировалась, поскольку вы должны активно разворачивать Optional
и решить этот случай. Null позволяет легко забыть о вещах, и хотя FindBugs помогает, я не думаю, что это решает проблему почти так же хорошо. Это особенно актуально, когда вы возвращаете значения, которые могут присутствовать или не присутствовать. Вы (и другие), скорее всего, забудете это other.method(a, b)
может вернуть null
значение, чем вы, вероятно, забудете, что a
может быть null
когда вы реализуете other.method
, возврате Optional
вызывающие абоненты не могут забыть этот случай, поскольку им приходится самим разворачивать объект.
По этим причинам мы рекомендуем вам использовать Optional
в качестве типа возврата для ваших методов, но не обязательно в аргументах вашего метода.
(Между прочим, это полностью извлечено из обсуждения здесь.)
Это действительно выглядит как Maybe
Узор монад от Хаскелла.
Вы должны прочитать следующее, 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, который является менее мощным.
Одна из веских причин его использования заключается в том, что он делает ваши нули очень значимыми. Вместо того, чтобы возвращать нуль, который может означать много вещей (например, ошибка, или сбой, или пустой, и т. Д.), Вы можете поставить "имя" в нуль. Посмотрите на этот пример:
Давайте определим базовый 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());
}
таким образом, в конце концов, это способ сделать нули значимыми и меньше двусмысленности.
Наиболее важным преимуществом Optional является то, что он добавляет больше деталей в контракт между исполнителем и вызывающей стороной функции. По этой причине полезно как для параметров, так и для типа возвращаемого значения.
Если вы делаете соглашение, чтобы всегда иметь Optional
для возможных нулевых объектов вы добавляете больше пояснений к случаям, таким как:
Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)
Контракт здесь четко указывает, что есть вероятность того, что результат не будет возвращен, но также показывает, что он будет работать с
from
а такжеto
как отсутствующий.Optional<Integer> maxPrime(Optional<Integer> from, Integer to)
В контракте указывается, что значение from является необязательным, поэтому отсутствующее значение может иметь особое значение, например, начинаться с 2. Я могу ожидать, что нулевое значение для
to
Параметр выдаст исключение.
Таким образом, хорошая часть использования Optional заключается в том, что контракт стал описательным (аналогично @NotNull
аннотации), но также формально, так как вы должны написать код .get()
справиться с Optional
,