Все мы, должно быть, сталкивались с NullPointerException в наших приложениях. Это исключение происходит, когда вы пытаетесь использовать ссылку на объект, которая не была инициализирована, инициализирована нулем или просто не указывает ни на один экземпляр. NULL просто означает «отсутствие значения». Скорее всего, римляне были единственными, кто не столкнулся с этой нулевой проблемой и начал считать на I, II, III … (без нуля). Возможно, они не могли смоделировать отсутствие яблок на своих рынках. [:-)]
«Я называю это своей ошибкой в миллиард долларов». — Сэр CAR Hoare, о своем изобретении нулевой ссылки
В этой статье мы обсудим одну из Java 8 функций для данного конкретного UseCase т.е. Optional . Эта статья была разделена на несколько разделов для ясности и различия между несколькими понятиями.
Discussion Points
1) What is the Type of Null?
2) What is wrong with just returning null?
3) How Java 8 Optionals provide the solution?
a) Creating Optional objects
b) Do something if a value is present
c) Default/Absent values and actions
d) Rejecting certain values Using the filter method
4) What is inside Optional make it work?
5) What is Optional trying to solve?
6) What is Optional not trying to solve?
7) How should Optional be used?
8) Conclusion
- Что такое тип нуля?
- Что не так с возвратом нулевого значения?
- Каково решение проблемы?
- Создание дополнительных объектов
- Сделать что-то, если присутствует необязательное значение
- Значения по умолчанию / отсутствующие значения и действия
- Отклонение определенных значений с использованием метода фильтра
- Что заставит его работать?
- Что пытается решить?
- Что решить не пытается?
- Как следует использовать?
- Вывод
Что такое тип нуля?
В Java мы используем ссылочный тип для получения доступа к объекту, а когда у нас нет конкретного объекта, на который мы можем ссылаться, тогда мы устанавливаем такую ссылку на null, чтобы подразумевать отсутствие значения. Правильно?
Использование нуля настолько распространено, что мы редко думаем об этом. Например, члены поля объектов автоматически инициализируются как null, а программисты обычно инициализируют ссылочные типы как null, когда у них нет начального значения, чтобы дать их, и, как правило, null используется каждый раз, когда мы не знаем или не делаем этого, не можем дать ссылку.
Что не так с возвратом нулевого значения?
Как правило, разработчики API помещают описательные документы Java в API и упоминают там, что API может возвращать нулевое значение, и в каком случае. Теперь проблема в том, что вызывающая сторона API могла по какой-то причине пропустить чтение javadoc и забыть о работе с нулевым регистром. Это будет ошибка в будущем наверняка.
И поверьте , это происходит часто и является одной из основных причин исключений нулевого указателя, хотя и не единственной. Итак, возьмите урок, который всегда читает java dcos API, когда вы используете его впервые (… по крайней мере) [:-)].
Теперь мы знаем, что ноль — это проблема в большинстве случаев, как лучше всего с ними справиться?
Хорошее решение — всегда инициализировать ссылки на ваш объект некоторым значением, а не нулевым. Таким образом, вы никогда не столкнетесь с NullPointerException . Справедливо. Но на практике у нас всегда нет значения по умолчанию для ссылки. Итак, как эти случаи должны быть обработаны?
Выше вопрос является правильным во многих смыслах. .
Каково решение проблемы?
Optional — способ заменить пустую ссылку T ненулевым значением. Optional может либо содержать ненулевую ссылку T (в этом случае мы говорим, что ссылка «присутствует»), либо он может ничего не содержать (в этом случае мы говорим, что ссылка «отсутствует»).
Optional<Integer> canBeEmpty1 = Optional.of(5); canBeEmpty1.isPresent(); // returns true canBeEmpty1.get(); // returns 5 Optional<Integer> canBeEmpty2 = Optional.empty(); canBeEmpty2.isPresent(); // returns false
Вы также можете просмотреть Optional как контейнер с одним значением, который либо содержит значение, либо его нет.
Важно отметить, что целью класса Optional является не замена каждой пустой ссылки. Вместо этого его цель состоит в том, чтобы помочь разработать более понятные API, так, что, просто читая сигнатуру метода, вы можете определить, следует ли ожидать необязательное значение. Это заставляет вас извлекать значение из Optional и работать с ним, и в то же время обрабатывать случай, когда option будет пустым. Ну, это как раз решение нулевых ссылок / возвращаемых значений, которые в конечном итоге приводят к
NullPointerException.
Ниже приведены некоторые примеры, чтобы узнать больше о том, как Optional должен быть создан и использован в коде вашего приложения.
Создание дополнительных объектов
Есть 3 основных способа создания Optional .
Optional<Integer> possible = Optional.empty();
Optional<Integer> possible = Optional.of(5);
Optional<Integer> possible = Optional.ofNullable(null); //or Optional<Integer> possible = Optional.ofNullable(5);
Сделать что-то, если присутствует необязательное значение
Optional<Integer> possible = Optional.of(5); possible.ifPresent(System.out::println);
if(possible.isPresent()){
System.out.println(possible.get());
}
Если бы Optional объект был пустым, ничего не печаталось бы.
Значения по умолчанию / отсутствующие значения и действия
//Assume this value has returned from a method Optional<Company> companyOptional = Optional.empty(); //Now check optional; if value is present then return it, //else create a new Company object and retur it Company company = companyOptional.orElse(new Company()); //OR you can throw an exception as well Company company = companyOptional.orElseThrow(IllegalStateException::new);
Отклонение определенных значений с использованием метода фильтра
Optional<Company> companyOptional = Optional.empty();
companyOptional.filter(department -> "Finance".equals(department.getName())
.ifPresent(() -> System.out.println("Finance is present"));
Метод фильтра принимает предикат в качестве аргумента. Если значение присутствует в Optional объекте и соответствует предикату, метод фильтра возвращает это значение; в противном случае он возвращает пустой Optional объект. Возможно, вы уже видели подобный шаблон, если использовали метод filter с интерфейсом Stream .
Хорошо, этот код выглядит ближе к постановке задачи, и на нашем пути нет подробных пустых проверок!
Ух ты!! Мы прошли долгий путь от написания болезненных вложенных нулевых проверок до написания декларативного кода, который можно компоновать, читать и лучше защищать от исключений нулевого указателя.
Что заставит его работать?
/** * If non-null, the value; if null, indicates no value is present */ private final T value;
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
Конструктор по умолчанию без аргументов — это определение private, поэтому вы не можете создать экземпляр Optional, за исключением трех указанных выше способов.
this.value = Objects.requireNonNull(value);
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
Аналогично, другие функции, определенные в классе Optional, работают только с атрибутом value. Просмотрите исходный код Optional.java для получения дополнительной информации.
Что пытается решить?
Optional — попытка уменьшить число исключений нулевого указателя в системах Java, добавив возможность создавать более выразительные API, учитывая, что иногда возвращаемые значения отсутствуют. Если бы он существовал с самого начала, сегодня большинство библиотек и приложений, скорее всего, справились бы с отсутствующими возвращаемыми значениями, уменьшая количество исключений нулевого указателя и общее количество ошибок в целом.
Используя Optional , пользователь вынужден думать об исключительном случае. Помимо повышения читабельности за счет присвоения нулевому имени, самым большим преимуществом является его защита от идиотов. Это заставляет вас активно думать об отсутствующем кейсе, если вы хотите, чтобы ваша программа вообще компилировалась, поскольку вы должны активно разворачивать Optional и устранять эти сбои.
Что решить не пытается?
Optional, это не механизм, позволяющий избежать всех типов нулевых указателей. Например, обязательные входные параметры методов и конструкторов все еще должны быть проверены.
Как и при использовании null, Optional не помогает передать значение отсутствующего значения. Таким образом, вызывающий метод все равно должен будет проверить javadoc API для понимания значения отсутствующего Optional, чтобы правильно с ним справиться.
- в слое модели домена (он не сериализуем)
- в DTO (это не сериализуемо)
- во входных параметрах методов
- в параметрах конструктора
- в слое модели домена (он не сериализуем)
- в DTO (это не сериализуемо)
- во входных параметрах методов
- в параметрах конструктора
Как следует использовать?
Optional следует использовать почти все время в качестве возвращаемого типа функций, которые могут не возвращать значение.
Это цитата из списка рассылки OpenJDK:
Optional не должен быть включен больше, чем необходимо для поддержки только идиомы опционального возврата.Кто-то предложил даже переименовать его в «OptionalReturn».
По сути, это означает, что Optional следует использовать в качестве возвращаемого типа определенных методов службы, репозитория или утилиты только там, где они действительно служат цели.
Вывод
Цель Optional состоит не в том, чтобы заменить каждую нулевую ссылку в вашей кодовой базе, а в том, чтобы помочь вам разработать более совершенные API, в которых, просто читая сигнатуру метода, пользователи могут определить, следует ли ожидать необязательное значение и обращаться с ним соответствующим образом.
