Перейти к основному содержимому

Руководство по Java 8 Дополнительно

· 17 мин. чтения

Задача: Наибольшая подстрока без повторений

Для заданной строки s, найдите длину наибольшей подстроки без повторяющихся символов. Подстрока — это непрерывная непустая последовательность символов внутри строки...

ANDROMEDA 42

1. Обзор

В этом руководстве мы собираемся показать класс Optional , представленный в Java 8.

Цель класса — предоставить решение на уровне типа для представления необязательных значений вместо пустых ссылок.

Чтобы глубже понять, почему мы должны заботиться о классе Optional , загляните в официальную статью Oracle .

2. Создание необязательных объектов

Существует несколько способов создания необязательных объектов.

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

@Test
public void whenCreatesEmptyOptional_thenCorrect() {
Optional<String> empty = Optional.empty();
assertFalse(empty.isPresent());
}

Обратите внимание, что мы использовали метод isPresent() , чтобы проверить, есть ли значение внутри необязательного объекта. Значение присутствует только в том случае, если мы создали необязательный с ненулевым значением. Мы рассмотрим метод isPresent() в следующем разделе.

Мы также можем создать необязательный объект с помощью статического метода () :

@Test
public void givenNonNull_whenCreatesNonNullable_thenCorrect() {
String name = "foreach";
Optional<String> opt = Optional.of(name);
assertTrue(opt.isPresent());
}

Однако аргумент, передаваемый методу of() , не может быть нулевым. В противном случае мы получим NullPointerException :

@Test(expected = NullPointerException.class)
public void givenNull_whenThrowsErrorOnCreate_thenCorrect() {
String name = null;
Optional.of(name);
}

Но если мы ожидаем каких -то нулевых значений, мы можем использовать метод ofNullable() :

@Test
public void givenNonNull_whenCreatesNullable_thenCorrect() {
String name = "foreach";
Optional<String> opt = Optional.ofNullable(name);
assertTrue(opt.isPresent());
}

Делая это, если мы передаем нулевую ссылку, она не генерирует исключение, а возвращает пустой необязательный объект:

@Test
public void givenNull_whenCreatesNullable_thenCorrect() {
String name = null;
Optional<String> opt = Optional.ofNullable(name);
assertFalse(opt.isPresent());
}

3. Проверка наличия значения: isPresent() и isEmpty()

Когда у нас есть необязательный объект, возвращенный из метода или созданный нами, мы можем проверить, есть ли в нем значение или нет, с помощью метода isPresent() :

@Test
public void givenOptional_whenIsPresentWorks_thenCorrect() {
Optional<String> opt = Optional.of("ForEach");
assertTrue(opt.isPresent());

opt = Optional.ofNullable(null);
assertFalse(opt.isPresent());
}

Этот метод возвращает true , если обернутое значение не равно null.

Кроме того, с Java 11 мы можем сделать обратное с помощью метода isEmpty :

@Test
public void givenAnEmptyOptional_thenIsEmptyBehavesAsExpected() {
Optional<String> opt = Optional.of("ForEach");
assertFalse(opt.isEmpty());

opt = Optional.ofNullable(null);
assertTrue(opt.isEmpty());
}

4. Условное действие с ifPresent()

Метод ifPresent() позволяет нам запустить некоторый код для обернутого значения, если оно не равно null . Перед опционом мы бы сделали:

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

Этот код проверяет, является ли переменная имени нулевой или нет, прежде чем выполнить какой-либо код для нее. Этот подход длительный, и это не единственная проблема — он также подвержен ошибкам.

В самом деле, что гарантирует, что после печати этой переменной мы не воспользуемся ею снова, а потом забудем выполнить проверку на null?

Это может привести к возникновению исключения NullPointerException во время выполнения, если в этот код попадет нулевое значение. Когда программа дает сбой из-за проблем с вводом данных, это часто является результатом плохой практики программирования.

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

Давайте теперь посмотрим, как приведенный выше код можно было бы реорганизовать в Java 8.

В типичном стиле функционального программирования мы можем выполнить действие над реально существующим объектом:

@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {
Optional<String> opt = Optional.of("foreach");
opt.ifPresent(name -> System.out.println(name.length()));
}

В приведенном выше примере мы используем только две строки кода, чтобы заменить пять, которые работали в первом примере: одну строку для переноса объекта в необязательный объект, а следующую для выполнения неявной проверки, а также для выполнения кода.

5. Значение по умолчанию с orElse()

Метод orElse() используется для извлечения значения, заключенного в опциональный экземпляр. Он принимает один параметр, который действует как значение по умолчанию. Метод orElse() возвращает обернутое значение, если оно присутствует, и его аргумент в противном случае:

@Test
public void whenOrElseWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("john");
assertEquals("john", name);
}

6. Значение по умолчанию с orElseGet()

Метод orElseGet() аналогичен orElse() . Однако вместо возвращаемого значения, если необязательное значение отсутствует, он принимает функциональный интерфейс поставщика, который вызывается и возвращает значение вызова:

@Test
public void whenOrElseGetWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
assertEquals("john", name);
}

7. Разница между orElse и orElseGet()

Для многих программистов, плохо знакомых с Optional или Java 8, разница между orElse() и orElseGet() непонятна. На самом деле, эти два метода создают впечатление, что они перекрывают друг друга по функциональности.

Однако между ними есть тонкая, но очень важная разница, которая может сильно повлиять на производительность нашего кода, если ее не понять.

Давайте создадим в тестовом классе метод getMyDefault() , который не принимает аргументов и возвращает значение по умолчанию:

public String getMyDefault() {
System.out.println("Getting Default Value");
return "Default Value";
}

Давайте рассмотрим два теста и рассмотрим их побочные эффекты, чтобы установить, где orElse() и orElseGet() перекрываются, а где они различаются:

@Test
public void whenOrElseGetAndOrElseOverlap_thenCorrect() {
String text = null;

String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
assertEquals("Default Value", defaultText);

defaultText = Optional.ofNullable(text).orElse(getMyDefault());
assertEquals("Default Value", defaultText);
}

В приведенном выше примере мы оборачиваем нулевой текст внутрь объекта Optional и пытаемся получить обернутое значение, используя каждый из двух подходов.

Побочный эффект:

Getting default value...
Getting default value...

В каждом случае вызывается метод getMyDefault() . Бывает так, что когда обернутое значение отсутствует, то и orElse() , и orElseGet() работают совершенно одинаково.

Теперь давайте запустим еще один тест, где значение присутствует, и в идеале значение по умолчанию даже не должно создаваться:

@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {
String text = "Text present";

System.out.println("Using orElseGet:");
String defaultText
= Optional.ofNullable(text).orElseGet(this::getMyDefault);
assertEquals("Text present", defaultText);

System.out.println("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getMyDefault());
assertEquals("Text present", defaultText);
}

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

Теперь давайте посмотрим на побочный эффект запуска этого кода:

Using orElseGet:
Using orElse:
Getting default value...

Обратите внимание, что при использовании orElseGet() для извлечения обернутого значения метод getMyDefault() даже не вызывается, поскольку содержащееся значение присутствует.

Однако при использовании orElse() независимо от того, присутствует обернутое значение или нет, создается объект по умолчанию. Итак, в этом случае мы только что создали один избыточный объект, который никогда не используется.

В этом простом примере создание объекта по умолчанию не требует значительных затрат, поскольку JVM знает, как с этим работать. Однако когда такой метод, как getMyDefault() , должен выполнить вызов веб-службы или даже запросить базу данных, стоимость становится очень очевидной.

8. Исключения с orElseThrow()

Метод orElseThrow() следует из orElse() и orElseGet() и добавляет новый подход к обработке отсутствующего значения.

Вместо того, чтобы возвращать значение по умолчанию, когда обернутое значение отсутствует, он выдает исключение:

@Test(expected = IllegalArgumentException.class)
public void whenOrElseThrowWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow(
IllegalArgumentException::new);
}

Ссылки на методы в Java 8 пригодятся здесь для передачи конструктору исключений.

В Java 10 представлена упрощенная версия метода orElseThrow () без аргументов . В случае пустого опциона выбрасывается исключение NoSuchElementException : ``

@Test(expected = NoSuchElementException.class)
public void whenNoArgOrElseThrowWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow();
}

9. Возврат значения с помощью get()

Последним подходом для извлечения обернутого значения является метод get() :

@Test
public void givenOptional_whenGetsValue_thenCorrect() {
Optional<String> opt = Optional.of("foreach");
String name = opt.get();
assertEquals("foreach", name);
}

Однако, в отличие от предыдущих трех подходов, get() может возвращать значение только в том случае, если обернутый объект не равен null ; в противном случае выдается исключение такого элемента:

@Test(expected = NoSuchElementException.class)
public void givenOptionalWithNull_whenGetThrowsException_thenCorrect() {
Optional<String> opt = Optional.ofNullable(null);
String name = opt.get();
}

Это главный недостаток метода get() . В идеале Необязательный должен помочь нам избежать таких непредвиденных исключений. Таким образом, этот подход противоречит целям опционального и, вероятно, будет объявлен устаревшим в будущем выпуске.

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

10. Условный возврат с фильтром()

Мы можем запустить встроенный тест для нашего обернутого значения с помощью метода фильтра . Он принимает предикат в качестве аргумента и возвращает необязательный объект. Если обернутое значение проходит проверку предикатом, то необязательное значение возвращается как есть.

Однако, если предикат возвращает false , он вернет пустой вариант :

@Test
public void whenOptionalFilterWorks_thenCorrect() {
Integer year = 2016;
Optional<Integer> yearOptional = Optional.of(year);
boolean is2016 = yearOptional.filter(y -> y == 2016).isPresent();
assertTrue(is2016);
boolean is2017 = yearOptional.filter(y -> y == 2017).isPresent();
assertFalse(is2017);
}

Метод фильтра обычно используется таким образом, чтобы отклонить упакованные значения на основе предопределенного правила. Мы могли бы использовать его, чтобы отклонить неправильный формат электронной почты или недостаточно надежный пароль.

Давайте посмотрим на другой содержательный пример. Скажем, мы хотим купить модем, и нас интересует только его цена.

Мы получаем push-уведомления о ценах на модемы с определенного сайта и сохраняем их в объектах:

public class Modem {
private Double price;

public Modem(Double price) {
this.price = price;
}
// standard getters and setters
}

Затем мы передаем эти объекты некоторому коду, единственной целью которого является проверка того, находится ли цена модема в пределах нашего диапазона бюджета.

Давайте теперь посмотрим на код без опциона :

public boolean priceIsInRange1(Modem modem) {
boolean isInRange = false;

if (modem != null && modem.getPrice() != null
&& (modem.getPrice() >= 10
&& modem.getPrice() <= 15)) {

isInRange = true;
}
return isInRange;
}

Обратите внимание на то, сколько кода нам нужно написать для этого, особенно в условии if . Единственная часть условия if , имеющая решающее значение для приложения, — это последняя проверка ценового диапазона; остальные проверки являются защитными:

@Test
public void whenFiltersWithoutOptional_thenCorrect() {
assertTrue(priceIsInRange1(new Modem(10.0)));
assertFalse(priceIsInRange1(new Modem(9.9)));
assertFalse(priceIsInRange1(new Modem(null)));
assertFalse(priceIsInRange1(new Modem(15.5)));
assertFalse(priceIsInRange1(null));
}

Кроме того, можно забыть о проверках на null в течение долгого дня, не получая никаких ошибок времени компиляции.

Теперь давайте посмотрим на вариант с Optional#filter :

public boolean priceIsInRange2(Modem modem2) {
return Optional.ofNullable(modem2)
.map(Modem::getPrice)
.filter(p -> p >= 10)
.filter(p -> p <= 15)
.isPresent();
}

Вызов карты просто используется для преобразования значения в какое-либо другое значение. Имейте в виду, что эта операция не изменяет исходное значение.

В нашем случае мы получаем объект цены из класса Model . Мы подробно рассмотрим метод map() в следующем разделе.

Прежде всего, если этому методу передается нулевой объект, мы не ожидаем никаких проблем.

Во-вторых, единственная логика, которую мы прописываем внутри его тела, — это именно то, что описывает название метода — проверка ценового диапазона. Факультативный позаботится обо всем остальном:

@Test
public void whenFiltersWithOptional_thenCorrect() {
assertTrue(priceIsInRange2(new Modem(10.0)));
assertFalse(priceIsInRange2(new Modem(9.9)));
assertFalse(priceIsInRange2(new Modem(null)));
assertFalse(priceIsInRange2(new Modem(15.5)));
assertFalse(priceIsInRange2(null));
}

Предыдущий подход обещает проверить ценовой диапазон, но должен сделать больше, чтобы защититься от присущей ему хрупкости. Следовательно, мы можем использовать метод фильтра , чтобы заменить ненужные операторы if и отклонить нежелательные значения.

11. Преобразование значения с помощью map()

В предыдущем разделе мы рассмотрели, как отклонить или принять значение на основе фильтра.

Мы можем использовать аналогичный синтаксис для преобразования значения Optional с помощью метода map() :

@Test
public void givenOptional_whenMapWorks_thenCorrect() {
List<String> companyNames = Arrays.asList(
"paypal", "oracle", "", "microsoft", "", "apple");
Optional<List<String>> listOptional = Optional.of(companyNames);

int size = listOptional
.map(List::size)
.orElse(0);
assertEquals(6, size);
}

В этом примере мы заключаем список строк в дополнительный объект и используем его метод карты для выполнения действия над содержащимся списком. Действие, которое мы выполняем, состоит в том, чтобы получить размер списка.

Метод карты возвращает результат вычислений, заключенный в необязательный элемент . Затем мы должны вызвать соответствующий метод для возвращенного параметра , чтобы получить его значение.

Обратите внимание, что метод фильтра просто выполняет проверку значения и возвращает необязательный параметр, описывающий это значение, только если оно соответствует заданному предикату. В противном случае возвращает пустой необязательный параметр. Однако метод карты принимает существующее значение, выполняет вычисление с использованием этого значения и возвращает результат вычисления, заключенный в необязательный объект:

@Test
public void givenOptional_whenMapWorks_thenCorrect2() {
String name = "foreach";
Optional<String> nameOptional = Optional.of(name);

int len = nameOptional
.map(String::length)
.orElse(0);
assertEquals(8, len);
}

Мы можем связать карту и фильтр вместе, чтобы сделать что-то более мощное.

Предположим, мы хотим проверить правильность ввода пароля пользователем. Мы можем очистить пароль с помощью преобразования карты и проверить его правильность с помощью фильтра :

@Test
public void givenOptional_whenMapWorksWithFilter_thenCorrect() {
String password = " password ";
Optional<String> passOpt = Optional.of(password);
boolean correctPassword = passOpt.filter(
pass -> pass.equals("password")).isPresent();
assertFalse(correctPassword);

correctPassword = passOpt
.map(String::trim)
.filter(pass -> pass.equals("password"))
.isPresent();
assertTrue(correctPassword);
}

Как мы видим, без предварительной очистки ввода он будет отфильтрован, но пользователи могут считать само собой разумеющимся, что начальные и конечные пробелы составляют ввод. Итак, мы преобразуем грязный пароль в чистый с помощью карты , прежде чем отфильтровать неправильные.

12. Преобразование значения с помощью flatMap()

Как и метод map() , у нас также есть метод flatMap() в качестве альтернативы для преобразования значений. Разница в том, что map преобразует значения только тогда, когда они развернуты, тогда как flatMap принимает обернутое значение и разворачивает его перед преобразованием.

Ранее мы создали простые объекты String и Integer для переноса в дополнительный экземпляр. Однако часто мы будем получать эти объекты от метода доступа к сложному объекту.

Чтобы получить более четкое представление о разнице, давайте взглянем на объект Person , который принимает данные о человеке, такие как имя, возраст и пароль:

public class Person {
private String name;
private int age;
private String password;

public Optional<String> getName() {
return Optional.ofNullable(name);
}

public Optional<Integer> getAge() {
return Optional.ofNullable(age);
}

public Optional<String> getPassword() {
return Optional.ofNullable(password);
}

// normal constructors and setters
}

Обычно мы создаем такой объект и заключаем его в необязательный объект, как мы это делали со строкой.

Как вариант, его можно вернуть нам другим вызовом метода:

Person person = new Person("john", 26);
Optional<Person> personOptional = Optional.of(person);

Теперь обратите внимание, что когда мы обертываем объект Person , он будет содержать вложенные экземпляры Optional :

@Test
public void givenOptional_whenFlatMapWorks_thenCorrect2() {
Person person = new Person("john", 26);
Optional<Person> personOptional = Optional.of(person);

Optional<Optional<String>> nameOptionalWrapper
= personOptional.map(Person::getName);
Optional<String> nameOptional
= nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);
String name1 = nameOptional.orElse("");
assertEquals("john", name1);

String name = personOptional
.flatMap(Person::getName)
.orElse("");
assertEquals("john", name);
}

Здесь мы пытаемся получить атрибут name объекта Person для выполнения утверждения.

Обратите внимание, как мы достигаем этого с помощью метода map() в третьем операторе, а затем обратите внимание, как мы делаем то же самое с методом flatMap() впоследствии.

Ссылка на метод Person::getName аналогична вызову String::trim , который мы использовали в предыдущем разделе для очистки пароля.

Единственное отличие состоит в том, что getName() возвращает необязательный , а не String, как это делала операция trim() . Это, в сочетании с тем фактом, что преобразование карты оборачивает результат в необязательный объект, приводит к вложенному необязательному объекту .

Поэтому при использовании метода map() нам нужно добавить дополнительный вызов для получения значения перед использованием преобразованного значения. Таким образом, необязательная оболочка будет удалена. Эта операция выполняется неявно при использовании flatMap .

13. Объединение опций в цепочку в Java 8

Иногда нам может понадобиться получить первый непустой необязательный объект из нескольких необязательных . В таких случаях было бы очень удобно использовать такой метод, как orElseOptional() . К сожалению, такая операция напрямую не поддерживается в Java 8.

Давайте сначала представим несколько методов, которые мы будем использовать в этом разделе:

private Optional<String> getEmpty() {
return Optional.empty();
}

private Optional<String> getHello() {
return Optional.of("hello");
}

private Optional<String> getBye() {
return Optional.of("bye");
}

private Optional<String> createOptional(String input) {
if (input == null || "".equals(input) || "empty".equals(input)) {
return Optional.empty();
}
return Optional.of(input);
}

Для того, чтобы связать несколько необязательных объектов и получить первый непустой в Java 8, мы можем использовать Stream API:

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturned() {
Optional<String> found = Stream.of(getEmpty(), getHello(), getBye())
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();

assertEquals(getHello(), found);
}

Недостатком этого подхода является то, что все наши методы get всегда выполняются, независимо от того, где в Stream появляется непустой option . ``

Если мы хотим лениво оценивать методы, переданные в Stream.of() , нам нужно использовать ссылку на метод и интерфейс Supplier :

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturnedAndRestNotEvaluated() {
Optional<String> found =
Stream.<Supplier<Optional<String>>>of(this::getEmpty, this::getHello, this::getBye)
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();

assertEquals(getHello(), found);
}

В случае, если нам нужно использовать методы, которые принимают аргументы, мы должны прибегнуть к лямбда-выражениям:

@Test
public void givenTwoOptionalsReturnedByOneArgMethod_whenChaining_thenFirstNonEmptyIsReturned() {
Optional<String> found = Stream.<Supplier<Optional<String>>>of(
() -> createOptional("empty"),
() -> createOptional("hello")
)
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();

assertEquals(createOptional("hello"), found);
}

Часто нам нужно вернуть значение по умолчанию в случае, если все связанные в цепочку необязательные s пусты. Мы можем сделать это, просто добавив вызов orElse() или orElseGet() :

@Test
public void givenTwoEmptyOptionals_whenChaining_thenDefaultIsReturned() {
String found = Stream.<Supplier<Optional<String>>>of(
() -> createOptional("empty"),
() -> createOptional("empty")
)
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.orElseGet(() -> "default");

assertEquals("default", found);
}

14. Дополнительный API JDK 9

В выпуске Java 9 в дополнительный API добавлено еще больше новых методов :

  • метод or() для предоставления поставщика, который создает альтернативный необязательный
  • Метод ifPresentOrElse() , который позволяет выполнить действие, если необязательно присутствует, или другое действие, если нет
  • метод stream() для преобразования необязательных данных в поток

Вот полная статья для дальнейшего чтения .

15. Неправильное использование опциональных s

Наконец, давайте рассмотрим заманчивый, хотя и опасный способ использования необязательных s: передача необязательных параметров в метод.

Представьте, что у нас есть список Person , и нам нужен метод для поиска в этом списке людей с заданным именем. Кроме того, мы хотели бы, чтобы этот метод соответствовал записям по крайней мере с определенным возрастом, если он указан.

Поскольку этот параметр является необязательным, мы используем этот метод:

public static List<Person> search(List<Person> people, String name, Optional<Integer> age) {
// Null checks for people and name
return people.stream()
.filter(p -> p.getName().equals(name))
.filter(p -> p.getAge().get() >= age.orElse(0))
.collect(Collectors.toList());
}

Затем мы выпускаем наш метод, и другой разработчик пытается его использовать:

someObject.search(people, "Peter", null);

Теперь разработчик выполняет свой код и получает исключение NullPointerException. Вот мы и вынуждены обнулить наш необязательный параметр, что противоречит нашей первоначальной цели в желании избежать такой ситуации.

Вот некоторые возможности, которые мы могли бы сделать, чтобы справиться с этим лучше:

public static List<Person> search(List<Person> people, String name, Integer age) {
// Null checks for people and name
final Integer ageFilter = age != null ? age : 0;

return people.stream()
.filter(p -> p.getName().equals(name))
.filter(p -> p.getAge().get() >= ageFilter)
.collect(Collectors.toList());
}

Там параметр по-прежнему необязательный, но мы обрабатываем его только за одну проверку.

Другой возможностью было бы создание двух перегруженных методов :

public static List<Person> search(List<Person> people, String name) {
return doSearch(people, name, 0);
}

public static List<Person> search(List<Person> people, String name, int age) {
return doSearch(people, name, age);
}

private static List<Person> doSearch(List<Person> people, String name, int age) {
// Null checks for people and name
return people.stream()
.filter(p -> p.getName().equals(name))
.filter(p -> p.getAge().get().intValue() >= age)
.collect(Collectors.toList());
}

Таким образом, мы предлагаем понятный API с двумя методами, выполняющими разные действия (хотя они имеют общую реализацию).

Таким образом, есть решения, позволяющие избежать использования Optional в качестве параметров метода. Намерение Java при выпуске Optional состояло в том, чтобы использовать его в качестве возвращаемого типа , указывая тем самым, что метод может возвращать пустое значение. На самом деле, некоторые инспекторы кода даже не одобряют практику использования Optional в качестве параметра метода .

16. Необязательно и сериализация

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

Кроме того, использование Optional в сериализуемом классе приведет к NotSerializableException . В нашей статье « Необязательный Java как возвращаемый тип» `` дополнительно рассматриваются проблемы с сериализацией.

А в разделе « Использование необязательного с Джексоном » мы объясним, что происходит, когда необязательные поля сериализуются, а также несколько обходных путей для достижения желаемых результатов.

17. Заключение

В этой статье мы рассмотрели большинство важных функций класса Optional в Java 8 .

Мы кратко рассмотрели некоторые причины, по которым мы предпочли бы использовать Optional вместо явной проверки null и проверки ввода.

Мы также узнали, как получить значение Optional или значение по умолчанию, если оно пусто, с помощью методов get() , orElse() и orElseGet() (и увидели важную разницу между двумя последними ).

Затем мы увидели, как преобразовать или отфильтровать необязательные объекты с помощью map(), flatMap() и filter() . Мы обсудили, что предлагает свободный API -интерфейс по выбору , поскольку он позволяет нам легко связывать различные методы.

Наконец, мы увидели, почему использование Optional в качестве параметров метода — плохая идея и как этого избежать.

Исходный код всех примеров в статье доступен на GitHub.