Класс Optional
является оберткой над объектами, которые могут принимать значение null.
Optional
является примером реализации монады Maybe
в Java.
Все методы класса Optional
можно разбить на несколько групп:
Фабричные методы
static Optional<T> empty()
- возвращает пустойOptional
с null вместо значения.static Optional<T> ofNullable(T)
- оборачивает объект, который может содержать в себе ссылку на null.static Optional<T> of(T)
- оборачивает объект, при этом не производит проверку на null.
Развертывание
T get()
- возвращает обернутое значение. Если внутри null, то будет выкинуто исключениеNoSuchElementException
.T orElse(T)
- заменяет тернарный оператор: если значение по вызывающей ссылке не равно null, то возвращается это значение, а если равно null, то возвращается объект, указанный в скобках.T orElseGet(Supplier<T>)
- если значение по вызывающей ссылке не равно null, то возвращается это значение, а если равно null, то возвращается объект, полученный с помощью аргумента-поставщика.T orElseThrow(Supplier<X>)
- возвращает значение. Если ссылка указывает на null, то бросает исключение, полученное с помощью аргумента-поставщика.
Потоковые (стримовые) методы
Optional<T> filter(Predicate<T>)
- проверяет соответствие значения внутриOptional
заданному условию, если нет, то возвращает пустойOptional
.Optional<U> map(Function<T, U>)
- если обернутый объект существует, то применяет к нему переданную функцию и возвращает значение, обернутое вOptional
, иначе возвращает пустойOptional
нужного типа.Optional<U> flatMap(Function<T, Optional<U>>)
- похож на предыдущий метод, но используется в случаях, если переданная функция сама по себе возвращает значениеOptional
, что позволяет избавиться от двойного обертывания. В контексте функциональных ЯП этот метод называется bind.
Прочие методы
boolean isPresent()
- сообщает есть ли значение внутриOptional
- true, или там пустая ссылка (null) - falsevoid ifPresent(Consumer<T>)
- Если внутри неnull
, то выполняет действие, указанное в скобках
Нововведения Java 9
Stream<Optional<T>> stream()
- возвращает стрим изOptional
. Метод позволяет легче оперироватьOptional
'ами в стримах.void ifPresentOrElse(Consumer<T>, Runnable)
- если элемент существует, то передает его в потребителя, а если отсутствует, то выполняет действие, переданное вторым аргументом.Optional<T> or(Supplier<Optional<T>>)
- Если объект существует, тоOptional
остается без изменений, а если нет, то он поставляется суплаером. Метод позволяет строить цепочки обработки:
Optional<String> result = wordFromCache
.or(() -> this.wordsRepo.getWord(id))
.or(() -> this.anotherWordsService.getWord(id))
.or(() -> this.getDefaultWord());
Класс используется, для того чтобы предупредить пользователей API, что метод может возвращать null вместо объектов.
Тем самым пользователи вынуждены будут проверить пришедшее значение на null прежде чем использовать его.
До появления Optional
экспертами рекомендовалось в тех случаях, когда метод может возвращать null вместо объекта,
возвращать одинарный массив или список, заставляя тем самым пользователя API проверять, а содержится ли объект в
этом массиве / списке.
Вместо подобной конструкции, использующей lazy оператор AND:
if (object != null && object.getName().equals("Vovan loh")) {
doSomething();
}
можно писать симпатично
Optional.ofNullable(object)
.filter(object.getName().equals("Vovan loh"))
.ifPresent(() -> doSomething());
Также можно использовать Optional
, если необходимо подменить какой-либо ресурс, который в результате предыдущих
операций мог оказаться null:
CustomObject result = Optional.ofNullable(object).orElseGet(CustomObject::new)
Такой подход позволяет не писать загромождающие код if'ы
CustomObject result = object;
if (result == null) {
result = new CustomObject();
}
Не рекомендуется:
- Использовать
Optional
в сеттерах и геттерах. - Заворачивать в
Optional
коллекцию или массив, лучше вместо этого передать пустую коллекцию или массив. - Передавать
Optional
в виде одного из аргументов метода. Вместо этого лучше сделать перегруженный вариант этого метода, где аргумент будет отсутствовать.
Рекомендуется:
- Использовать
Optional
когда нужно показать, что какое-то возвращаемое значение может не иметь значения и это абсолютно нормально. В таком случае null в этом поле будет говорить об ошибке, а Optional.empty() о нормальном поведении. Это и есть главное предназначениеOptional
под задумке архитекторов Java. - Использовать
Optional
там, где раньше были "магические числа (объекты)". - В методах-генераторах данных. Например при получении данных из БД.
- Использовать
Optional
для выборочного подключения зависимости к компоненту в рантайме.
- Optional не реализует интерфейс
Serializable
, что ограничивает использование объектов типаOptional
в качестве сериализованных данных. - Некоторые фреймворки, использующие рефлексию, могут неверно сравнивать между собой объекты
Optional
. Jackson и Hibernate поддерживают его, но другие менее популярные библиотеки могут сломаться.
- Javadoc на класс
Optional
(EN). - Гайд по
Optional
от Oracle (EN). Oracle - Паттерны и антипаттерны использования
Optional
(EN). DZone - Ответ архитектора Java по use-case'ам использования
Optional
(EN). StackOverflow - Серия статей: Объект в футляре или Optional в Java 8 и Java 9. (RU) (2018):