Первый: Ты действительно уверен, что эта переменная никогда не будет null
Второй: конечно!
NullPointerException
Ведь так начинается Java код-ревью? NPE всегда является кошмаром для Java разработчиков. Рассмотрим не самый простой пример кода:
public interface Service { public boolean switchOn(int timmer); public boolean switchOff(int timmer); //другие элементы } public class RefrigeratorService implements Service { // ... } public class HomeServices { private static final int NOW = 0; private static HomeServices service; public static HomeServices get() { //проверка на Null #1 if(service == null) { service = new HomeServices(); } return service; } public Service getRefrigertorControl() { return new RefrigeratorService(); } public static void main(String[] args) { /* получаем Home Services */ HomeServices homeServices = HomeServices.get(); //проверка на Null #2 if(homeServices != null) { Service refrigertorControl = homeServices.getRefrigertorControl(); //проверка на Null #3 if (refrigertorControl != null) { refrigertorControl.switchOn(NOW); } } } }Как видно вышел, в коде несколько проверок на Null. Конечно все это сделано, чтобы код был более надежным во всех ситуация. Это необходимо, или ...
Есть путь лучше?
В общем - да! В Java 8 представили java.util.Optional<T>. Это контейнер, который может содержить или не содержить null значения. Java 8 предоставляет нам более безопасный способ взаимодействовать с объектами, которые в некоторых случаях могут быть null. Данное решение вхоновлено идеями Haskell и Scala.
Если коротко - класс Optional включает методы, для корректной работы в двух случаях, когда значение существует и когда отсутствует. Преимущество, в сравнении с null проверкой, заключается в том, что класс Optional, заставляет обдумывать случаи, когда значение отсутствует. Т.о. получится избежать непредвиденных NPE.
В примере выше у нас есть фабрика HomeService, которая обрабатывает несколько устройств, доступных в доме. Но эти устройства функционально могут быть досутпны или не доступны. Т.е. может случиться NPE. Вместо того, чтобы добавлять проверку на null, обернем объект в Optional.
Мы часто используем тернарный оператор, чтобы проверить на null и значение по умолчанию, если null. Optional предоставляет то же самое без проверок. Optional.orElse(defaultObj) возвращает defaultObj, если в Optional значение null. Используем это в нашем коде:
В итоге вот наш код без NPE и null проверок:
PS: это мой перевод данной статьи
Если коротко - класс Optional включает методы, для корректной работы в двух случаях, когда значение существует и когда отсутствует. Преимущество, в сравнении с null проверкой, заключается в том, что класс Optional, заставляет обдумывать случаи, когда значение отсутствует. Т.о. получится избежать непредвиденных NPE.
В примере выше у нас есть фабрика HomeService, которая обрабатывает несколько устройств, доступных в доме. Но эти устройства функционально могут быть досутпны или не доступны. Т.е. может случиться NPE. Вместо того, чтобы добавлять проверку на null, обернем объект в Optional.
Оборачивание в Optional<T>
Рассмотрим получение объекта из фабрики. Вместо возвращение инстанса элемента, обернем его в Optional. Это позволит пользователю API, понимать, что возвращаемый сервис и может быть функционально доступным или нет. Используем безопасно:
public Optional<Service> getRefrigertorControl() { Service s = new RefrigeratorService(); //... return Optional.ofNullable(s); }Как вы видите, Optional.ofNullable предоставляет простой путь получить обернутый объект. Есть другие пути, чтобы получить оберный объект в Optional: Optiona.empty() или Optional.of(). Первый возвращает пустой объект, вместо null, а второй - оборачивает не null значения объектов.
Как это помогает избежать null проверок?
Обернув объект в Optional единожды, получаем множество полезных методов для вызова функционала обернутого объекта, без NPE.
Optional ref = homeServices.getRefrigertorControl(); ref.ifPresent(HomeServices::switchItOn);Optiona.ifPresent вызывает потребителя (Consumer) по ссылке, если значение не null, иначе ничего не делает. Это очень красиво и легко для понимания. В примере выше, HomeService.switchOn(Service) вызывается, только если Optional содержить не null значение.
Мы часто используем тернарный оператор, чтобы проверить на null и значение по умолчанию, если null. Optional предоставляет то же самое без проверок. Optional.orElse(defaultObj) возвращает defaultObj, если в Optional значение null. Используем это в нашем коде:
public static Optional<HomeServices> get() { service = Optional.of(service.orElse(new HomeServices())); return service; }Сейчас HomeServices.get() делает то же самое, но лучшим способов. Он проверяет, проинициализирован ли сервис. Optional<T>.orElse(T) помогает вернуть значение по умолчанию.
В итоге вот наш код без NPE и null проверок:
import java.util.Optional; public class HomeServices { private static final int NOW = 0; private static Optional<HomeServices> service; public static Optional<HomeServices> get() { service = Optional.of(service.orElse(new HomeServices())); return service; } public Optional<Service> getRefrigertorControl() { if(ServiceDiscovery.isAvaiable("refrigetor")) { Service s = RefrigeratorService.get(); return Optional.ofNullable(s); } return Optional.empty(); } public static void main(String[] args) { Optional<HomeServices> homeServices = HomeServices.get(); if(homeServices.isPresent()) { Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl(); refrigertorControl.ifPresent(HomeServices::switchItOn); } } public static void switchItOn(Service s){ //... } }
PS: это мой перевод данной статьи