Источник: Nuances of Programming
В 1964 году британский ученый-компьютерщик Тони Хоар изобрел ссылки на нулевые указатели (Null Pointer References).
Исключение Null Pointer Exception составляет львиную долю всех исключений, которые возникают в продакшне. Оно было реализовано во многих языках программирования, включая C, C++, C#, JavaScript, Java и другие.
Потеря денег, времени и человеческих ресурсов, которые уходят на его исправление, побудила Хоара назвать его “ошибкой на миллиард долларов”.
Java — один из языков программирования, где реализованы ссылки на нулевые указатели. Если вы разрабатывали на Java, то наверняка их встречали. Неважно, новичок ли вы в Java или за плечами у вас десять лет опыта — всегда есть вероятность, что вы столкнетесь с ошибкой Null Pointer Exception.
Optional в Java
Optional — это API, предоставленный в Java 8. При правильном применении он способен решить проблему Null Pointer Exception.
API Optional реализует функциональное программирование и использует функциональный интерфейс.
Прежде чем мы продолжим, пожалуйста, обратите внимание, что примеры в этой статье приводятся на Java 11. Если вы пользуетесь другой версией Java, некоторые методы могут не существовать или вести себя иначе.
Пустой Optional
Пустой элемент optional — это основной способ избежать исключения Null Pointer Exception при использовании API Optional.
В потоке Optional любой null будет преобразован в пустой Optional. Пустой элемент Optional больше не будет обрабатываться. Вот как мы можем избежать исключения NullPointerException.
Подробнее о том, как ведет себя пустой Optional, мы поговорим позже.
Создадим объект Optional
Существует три способа инициализации объекта Optional:
- Optional.of(T)
- Optional.ofNullable(T)
- Optional.empty()
Optional.of
Optional.of принимает в качестве параметра любой тип с ненулевым значением. Чтобы создать объект Optional с помощью Optional.of, нужно просто передать значение в параметр.
Будьте осторожны, когда передаете значение Optional.of. Помните, что Optional.of не принимает в качестве параметра нулевые значения. Если вы попытаетесь передать нулевое значение, оно вызовет исключение NullPointerException.
Optional.ofNullable
Optional.ofNullable аналогичен Optional.of. Он принимает любой тип. Разница в том, что с помощью Optional.ofNullable параметру возможно передать нулевое значение.
Когда Optional.ofNullable инициализируется с объектом null, он возвращает пустой Optional.
Optional.empty
Пустой Optional можно инициализировать через Optional.empty().
Доступ к Optional.
Есть несколько способов получить значение Optional.
get
Простой метод. Метод get вернет значение Optional, если оно присутствует, и вызовет исключение NoSuchElementException, если значения не существует.
orElse
Если вы предпочитаете значение по умолчанию в случае, когда Optional пуст, вы можете воспользоваться методом orElse.
orElseGet
orElseGet очень похож на метод orElse. Только orElseGet принимает в качестве параметра Supplier<T>.
orElseThrow
orElseThrow вернет значение Optional или выдаст исключение, если значение Optional пустое.
Обработка Optional
Есть много способов обработки и преобразования Optional. В этом разделе мы рассмотрим наиболее распространенные методы.
Как уже говорилось в начале статьи, пустой элемент Optional не будет обрабатываться в потоке. Сейчас мы увидим это на примерах.
map
Метод map чаще других применяется при обработке объекта Optional. В качестве параметра он принимает Function<? super T, ? extends U> и возвращает Optional<U>. Это означает, что в вашей функции может быть любой тип параметра, а возвращаемое значение будет обернуто в Optional внутри метода map.
Если вы попытаетесь вернуть нулевое значение в функции <? super T,? extends U>, метод map вернет пустой Optional.
Пустой Optional не будет обработан map. Это подтверждается следующим тестом:
flatMap
Этот метод похож на map, но flatMap не будет переносить возвращаемое значение функции в Optional. Метод flatMap принимает в качестве параметра Function<? super T, ? extends Optional<? extends U>>. Это означает, что вам нужно будет определить функцию, которая принимает любой тип и возвращает Optional.
Как правило, метод flatMap пригождается, когда ваш код вызывает другой метод, возвращающий объект Optional.
filter
В предыдущем примере с flatMap мы использовали декларативный стиль для дифференциации возвращаемого значения метода getString. Но можно переписать это в функциональном стиле с помощью метода filter.
ifPresent
Метод ifPresent принимает Consumer, который будет выполняться только в том случае, если Optional не пуст.
Чего следует избегать
Если вы хотите использовать Optional в своем коде, вам стоит избегать некоторых критичных вещей.
Не создавайте метод, который принимает Optional
Создание метода, который принимает Optional в качестве параметра, может привести к возникновению той самой проблемы, которую он предположительно решает: NullPointerException.
Если пользователь метода с параметром Optional не знает об этом, он может передать методу null вместо Optional.empty(). Обработка null приведет к исключению NullPointerException.
Получение значения без проверки
Если вы задействуете Optional, то лучше по возможности избегать метода get. Если по какой-то причине он вам все-таки нужен, убедитесь, что вы сначала проверили его с помощью метода isPresent, потому что если применить get на пустом Optional, он вызовет исключение NoSuchMethodException.
Заключение
Спасибо, что дочитали до конца! Optional — мощная функция, о которой стоит знать каждому Java-разработчику. Если вы станете правильно применять функции optional от начала до конца, то вряд ли еще когда-либо столкнетесь с исключением NullPointerException.
Optional также задействован в качестве базы других больших библиотек, таких как Reactor и RXJava, поэтому знание того, как работает Optional, поможет вам разобраться в них тоже.
Репозиторий с примерами из этой статьи вы можете найти здесь: https://github.com/brilianfird/java-optional
Читайте также:
Перевод статьи Brilian Firdaus: “Avoiding the Null Pointer Exception With Optional in Java”