Допустим, вы должны использовать интерфейс для написания некоторой логики, и вы видите определение интерфейса ниже.
public interface OrderService {
Order findOrderWithId(long orderId);
}
Что первое, что приходит на ум? Скорее всего, это будет вопрос о том, что произойдет, если ордер с запрошенным идентификатором заказа не существует.
Чтобы найти ответ, вы можете:
- Проверьте документацию по интерфейсу (Что, если документация отсутствует?), Или
- Посмотрите в исходный код (Что делать, если исходный код не доступен?)
В любом из случаев наибольшая вероятность того, что интерфейс вернет нулевой ордер .
Клиентская логика, которую вы будете писать для такого интерфейса, будет выглядеть примерно так:
Order order = orderService.findOrderWithId(orderId);
if (order == null) throw new OrderNotFoundException();
order.getTotalAmount();
... // do further processing
В большинстве случаев жизнь разработчика не легка. Есть крайние сроки, которые вы должны соблюдать, и под давлением доставки вы можете забыть добавить эту нулевую проверку if (order == null)
. И тогда вы можете увидеть страшные, NullPointerException
когда:
- вы запускаете свои юнит-тесты (похлопываете по спине за написание юнит-тестов ), или
- ваш код проверяется QA (спасибо QA за обнаружение ошибки в коде), или
- в худшем случае, когда ваш код запущен в производство.
Представление о возможности отсутствия значения с помощью необязательного
Это где Optional
приходит на помощь. Он был частью библиотек Google Guava долгое время. Это также недавно было включено в Java 8 . Давайте попробуем переписать интерфейс, используя версию Java 8 Optional
.
public interface OrderService {
Optional<Order> findOrderWithId(long id);
}
Теперь, если вы используете этот интерфейс для написания некоторой логики, вы заметите, что сам возвращаемый тип говорит вам, что он может присутствовать или не присутствовать.
Посмотрим, как будет выглядеть клиентский код на таком интерфейсе. Приведенный ниже код специально сделан подробным с целью иллюстрации. Более краткая версия также будет следовать.
Optional<Order> orderOptional = orderService.findOrderWithId(orderId);
if (!orderOptional.isPresent()) throw new OrderNotFoundException();
Order order = orderOptional.get();
order.getTotalAmount();
... // do further processing
Обратите внимание , что клиентский код от интерфейса называет get()
на , Optional
чтобы получить объект , который также заставляет писатель кода клиента , чтобы думать и обрабатывать случаи , то значение нет. Вы можете использовать isPresent()
для проверки наличия или отсутствия значения.
Более краткая версия приведенного выше кода будет.
Order order = orderService.findOrderWithId(orderId)
.orElseThrow(OrderNotFoundException::new);
order.getTotalAmount();
... // do further processing
Есть несколько заводских методов, таких как Optional.of(T value)
, Optional.ofNullable(T value)
и Optional.empty()
которые могут использоваться на стороне производителя. Со стороны потребителей метода экземпляра любит get()
, orElse(T other)
, orElseGet(Supplier<? extends T> other)
и orElseThrow(Supplier<? extends X> exceptionSupplier)
может быть использована.
Резюме
Используйте Optional
для представления отсутствия или наличия значения. Используйте его для указания типов возврата при разработке интерфейсов или API. Его использование дает следующие преимущества:
- Код становится документацией для любого клиента, использующего код.
- Код клиента становится менее подверженным ошибкам и может избежать
NullPointerException
s из-за халатности.