В Java 8 появился класс «Optional» для лучшей обработки нулевых ссылок. Необязательный — это объект-контейнер, который может содержать или не содержать значение. Затем класс предоставляет методы для оператора данных, которые он может содержать.

Необязательный параметр можно создать для любого типа данных в Java. Для примитивных типов данных необязательный доступен как вариант для int, double и long в форме `OptionalInt`, `OptionalDouble`, `OptionalLong`.

Однако в этих вариантах отсутствуют методы карты и фильтрации, которые предоставляет Необязательный, но эти операции можно выполнить над этими вариантами, преобразовав их в поток с помощью метода stream().

Чтобы создать экземпляр дополнительного класса, нам нужно использовать статические методы of() или ofNullable() со значением и пустым необязательным параметром, используя empty( ) метод.

 Optional<String> strOptional = Optional.of("data");
 Optional<String> emptyOptional = Optional.empty();
 Optional<String> nullableOptional = Optional.ofNullable(null); //Returns Optional.EMPTY

До класса Необязательный ссылку нужно было проверять на нулевое значение с помощью явной проверки на нулевое значение. Это можно заменить вызовом isPresent(), чтобы проверить, присутствует ли значение в экземпляре Необязательного.

 if(value != null) {
    System.out.println(value);
 }

можно заменить на

 if(optionalValue.isPresent()) {
    System.out.println(optionalValue.get());
 }

Приведенный выше пример также можно написать с использованием метода ifPresent(), который принимает Consumer и выполняется только в том случае, если необязательный параметр имеет значение.

 optionalValue.ifPresent(v -> System.out.println(v));

Значение внутри необязательного параметра можно получить с помощью функции get(). Вызов get для пустого необязательного параметра приведет к исключению.

Если существует вероятность того, что необязательный параметр пуст, можно вызвать метод orElse(), указав значение по умолчанию. Другой вариант — вызвать метод or(), который принимает поставщика необязательного значения.

Метод orElseGet() принимает поставщика, который выполняется методом orElseGet() для получения значения по умолчанию, если параметр «Необязательный» пуст.

Если необходимо создать пользовательское исключение, если значение отсутствует, тогда у нас есть orElseThrow(), который принимает функцию поставщика, которая предоставляет экземпляр экземпляра Throwable. Метод orElseThrow() выдает исключение NoSuchElementException, если поставщик не указан.

optionalValue.or(() -> Optional.of("defaultValue")); //Return the result of the Supplier if optionalValue is empty.
optionalValue.orElse("defaultValue"); //Returns the default value in case the Optional value is empty. 
optionalValue.orElseGet(() -> "defaultValue"); // Accepts a Supplier lambda or method which returns a default value.
optionalValue.orElseThrow() // Throws NoSuchElementException if the optional is empty.
optionalValue.orElseThrow(() -> new RuntimeException()); //Throws the custom exception if optional is empty.

В Java 9 опциональный также представил метод stream(), который обеспечивает взаимодействие между опциональным и потоковым. Метод stream() возвращает поток со значением, присутствующим в необязательном параметре, или возвращает пустой поток.

В Java 9 также появился метод ifPresentOrElse(), который принимает Consumer и Runnable. Потребитель выполняется, если значение присутствует, а Runnable выполняется, если необязательный параметр пуст.

optionalValue.ifPresentOrElse(v -> System.out.println("Optional value is " + v), () -> System.out.println("Optional is Empty"));

карта()

Метод map() в основном используется для преобразования данных, присутствующих внутри дополнительного объекта. Он принимает функцию, которая может преобразовать необязательные данные в тот же тип или даже в другой тип. Функция, переданная методу map(), применяется только в том случае, если экземпляр Необязательного содержит какие-либо данные. Если необязательный параметр пуст, метод map() возвращает пустой необязательный параметр. Эта функция очень полезна, поскольку позволяет избежать необходимости проверки значений NULL или вызова isPresent(), который в противном случае потребовался бы перед выполнением какой-либо операции с данными внутри экземпляра необязательного.

Optional<String> optionalString = Optional.of("Hello, Optional!");
int length = 0;
if(optionalString.isPresent()) {
   optionalString.get().length();
}

можно переписать как

Optional<String> optionalString = Optional.of("Hello, Optional!");
int length = optionalString.map(s -> s.length()).orElse(0); //The function here is applied only if the optional has a value.

Поскольку метод map() также возвращает экземпляр необязательного. Это очень полезно при объединении преобразований без необходимости проверки нулевых/пустых значений.

Optional.of(1).map(i -> i * 2).map(i -> i * 10).map(i -> i / 5)

плоская карта()

Подобно методу map(), flatMap() также используется для преобразования значений. Однако ключевое различие между map() и flatMap() заключается в типах их функций. В то время как метод map() принимает функцию, которая не возвращает необязательный параметр, метод flatMap() принимает функцию, которая возвращает необязательный параметр. Более подробно: при использовании map() результат функции заворачивается в необязательный параметр и возвращается. Напротив, при использовании flatMap() результат функции, который является необязательным, подвергается обработке для устранения внутреннего уровня необязательности. В результате получается «сглаженный» вывод, в котором внутренний необязательный параметр удаляется, а данные заключаются во внешний необязательный параметр.

В качестве иллюстрации рассмотрим следующий фрагмент кода.

Function<String,Optional<String>>helloGreeter=name->Optional.of("Hello,"+name);
Optional<String>name=Optional.of("John");
Optional<Optional<String>>result=name.map(helloGreeter);

В приведенном выше примере результатом является экземпляр Необязательного, обертывающий другой экземпляр Необязательного. Чтобы избежать этой вложенной опциональной структуры, код можно переписать с помощью FlatMap():

Optional<String>result=name.flatMap(helloGreeter);

Несколько вещей, которые следует учитывать при использовании необязательного для написания кода.

  • Необязательный не является сериализуемым и поэтому не должен использоваться в качестве типа поля в классе. Этот ответ на stackoverflow подробно описывает, почему необязательное никогда не предназначалось для сериализации.
    — Модуль Jackson JDK8 предоставляет решение для сериализации необязательных полей в JSON путем обработки пустых необязательных полей в нулевые и непустые поля с их значениями. Здесь — статья, подробно описывающая использование модуля jackson jdk8 с необязательным.
  • Необязательное значение никогда не следует использовать в качестве параметра метода, так как любой клиент может передать нулевое значение для необязательного параметра, что потребует записи нулевых проверок в параметре метода.