JavaRush /Java блог /Random UA /Кава-брейк #2. Клас Optional в Java: як його правильно ви...
Yuliia Tunik
42 рівень
Киев

Кава-брейк #2. Клас Optional в Java: як його правильно використовувати

Стаття з групи Random UA
Джерело: Medium Ця публікація присвячена проблемам застосування класу Optional, який був представлений у Java 8 як ключовий елемент Stream API. Кава-брейк #2. Клас Optional в Java: як його правильно використовувати - 1

Суть проблеми з класом Optional

Stream API було представлено в Java 8, і я припускаю, що кожен розробник Java мав досвід використання цього API у своїй розробці. Розглянемо простий приклад.

List<String> names = List.of("vinod", "madubashana");
names.stream()
     .filter(name -> name.startsWith("a"))
     .findFirst();
Тепер, коли ви використовуєте Stream API, яким має бути значення, що повертається, у наведеному вище випадку? Звичайно, тут немає іншого варіанту, окрім повернення null. Потоки можуть бути порожніми або вони можуть стати порожніми в процесі виконання проміжних операцій, таких як фільтри, як ви бачите в наведеному вище випадку. Але якщо він призначений для повернення null, API не вказує, що він може мати значення null. Що робити, якщо вам потрібно явно вказати, що значення, яке повертається, може бути порожнім через API?

Рішення проблеми

Єдиним рішенням є введення нового класу-обгортки (wrapper class), і це те, що за замовчуванням надається в Java 8 при використанні класу Optional. Це не рішення всіх проблем, пов’язаних із значеннями null. Воно призначене для представлення типів повернення наших API, щоб вказати, що воно може бути порожнім, і це вимагає обережності під час роботи з ними. До Java 8 ми писали власні реалізації, але тепер нам цього робити не потрібно. Таким чином, клас Optional надає API для представлення значення, яке також може бути порожнім.

Створення

Optional — це final-клас, тому ми не можемо створити його підклас. Конструктори також є private, а для створення екземплярів надаються методи статичних факторів (static factor methods).

Optional.empty();
Optional.of("vinod");
Optional.ofNullable(null);
Метод empty можна використовувати для створення порожнього Optional. Ніколи не використовуйте null для посилання на екземпляр Optional, завжди використовуйте метод empty для створення порожнього Optional. Якщо ви повертаєте null з методу, де тип повернення є Optional, весь сенс введення Optional втрачається. Метод of можна використовувати лише для обгортання non-null посилання. Якщо передається значення null, тоді з’явиться виключення NullPointerException. Метод ofNullable дозволяє передавати null посилання. Це не означає, що ви можете використовувати значення null разом з Optional. Він дозволяє передавати значення null, але коли ви передаєте таке значення, він повертає порожній Optional, а не Optional, який містить значення null.

Серіалізація Java

Optional — це не серіалізований клас. Тому ви не можете використовувати серіалізацію Java. Це зроблено навмисно, щоб у найближчому майбутньому підтримати об'єкти значень, які є частиною Проєкту Valhalla.

Спеціалізовані класи Optional

Щоб уникнути boxing та unboxing, існують спеціалізовані класи Optional, такі як OptionalInt, OptionalLong та OptionalDouble. Ви можете використовувати ці класи, якщо вам це треба для кращої продуктивності.

Використання Optional


String value = optional.get();
if (optional.isPresent()) {
    String value1 = optional.get();
}


optional.ifPresent(val -> System.out.println(val));
optional.ifPresentOrElse(System.out::println,
     () -> System.out.println("no value"));
String alternative = optional.orElse("alternative");
String alternative1 = optional.orElseGet(() -> "alternative");
Optional<String> alternativeOptional = optional.or(
                () -> Optional.of("alternative"));
String value2 = optional.orElseThrow();
String value3 = optional.orElseThrow(
                () -> new RuntimeException("exception"));
Давайте зрозуміємо деякі важливі пункти щодо використання методів у класі Optional. Метод get є найпростішим способом, якого завжди намагаються уникнути. Цей метод викличе NoSuchElementException, якщо ви викликаєте його для порожнього Optional. Це також вказує на те, що це не найкраще рішення для обробки NullPointerException. Тому потрібно викликати методи isPresent або isEmpty, щоб перевірити порожнечу, а вже потім викликати метод get. Однак, існують інші, кращі альтернативні способи. Метод ifPresent спрощує цю перевірку і концентровано діє за допомогою споживача (consumer). Споживач буде викликаний, лише якщо Optional не порожній. ifPresentOrElse приймає Runnable як другий аргумент, який може бути використаний для виконання деякої логіки, коли Optional порожній. orElse та orElseGet можна використовувати для отримання значення за замовчуванням, коли Optional порожній. Метод or стає зручним, коли ваш тип повернення є Optional і вам потрібно повернути новий варіант, коли запланований варіант для повернення стає порожнім. І нарешті, за допомогою методів orElseThrow ви можете легко створювати виключення під час виконання.

Операції з Optional

Ще одна перевага наявності Optional замість повернення null — це можливість виконувати операції перетворення без перевірок на null. Це надає можливість писати плавний ланцюжок викликів методів. Подивіться на приклад нижче:

String name = optional.filter(name -> name.startsWith("ab"))
                .map(String::toUpperCase)
                .orElse(null);
Ще одна доступна операція — це flatMap, яку можна використовувати для сплющення вкладених Optional, що подібно до операції flatMap у Stream API. Метод stream також доступний у цьому класі, він може перетворити Optional у потік Java. Після цього можна використовувати всі операції, пов'язані з потоками.

Рекомендації по роботі з Optional

  • Найкраще місце для використання Optional — це тип повернення методу. Ви, можливо, вже зіткнулися з цим, використовуючи багато відкритих бібліотек.
  • Не підходить для аргументів методів та конструкторів. Це тому, що це ускладнює код клієнта і додає непотрібну складність.
  • Не підходить як елементи колекції. Це також ускладнює читання, коли ми пишемо операції споживання в потоках.
  • Не обгортайте колекції за допомогою Optional. Замість цього, повертайте порожній об'єкт колекції, використовуючи деякі допоміжні методи, такі як Collections.emptyList().
  • Не намагайтеся бути хитрим, використовуючи трюки, наприклад, обгортання об'єктів, які можуть бути null, і використання API класу Optional для обробки коду. Прості перевірки на null роблять те саме, і це призводить до набагато більш читабельного коду.
  • Не підходить для полів класу (class fields). Це надмірне використання Optional. Клас Optional є обгорткою, що означає, що він споживає більше пам'яті, ніж звичайні посилання. Його надмірне використання може уповільнити вашу програму. Якщо використовуваний клас є серіалізованим, тоді у нас виникає ще одна проблема, оскільки Optional не є серіалізованим. Замість цього, ви можете повертати Optional з методів getter, щоб вказати, що поля можуть бути порожніми.
  • Спробуйте уникати вкладених (nested) Optional, які роблять код незрозумілим при подібному використанні.
  • Не використовуйте операції, чутливі до ідентичності, такі як перевірка рівності за допомогою ==. Це може призвести до непередбачуваних результатів. Метод equals працює набагато краще, порівнюючи значення всередині Optional. Таким чином, його можна використовувати в модульних тестах, як assertEquals, не витягуючи значення з Optional.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ