JavaRush /Java блог /Random UA /Кава-брейк #161. Як обробляти Null в Java за допомогою Op...

Кава-брейк #161. Як обробляти Null в Java за допомогою Optional

Стаття з групи Random UA
Джерело: Medium Завдяки цій статті ви зможете краще зрозуміти призначення Optional під час роботи з кодом Java. Кава-брейк #161.  Як обробляти Null в Java за допомогою Optional - 1Коли я вперше почав працювати з Java-кодом, мені часто рекомендували використовувати Optional. Але я тоді погано розумів, чому використання Optional краще, ніж реалізації обробки для значень null. У цій статті я хочу поділитися з вами тим, навіщо, на мою думку, нам усім варто більше використовувати Optional, і як уникнути надмірної оптионалізації коду, яке шкодить його якості.

Що таке Optional?

Параметр Optional використовується для перенесення об'єктів та забезпечення обробки посилань на null за допомогою різних API-інтерфейсів. Давайте подивимося на уривок коду:
Coffee coffee = new Coffee();
Integer quantity = coffee.getSugar().getQuantity();
У нас є екземпляр Coffee , в якому ми отримуємо деяку кількість sugar з екземпляра об'єкта Sugar . Якщо ми припустимо, що значення кількості ніколи не встановлювалося в конструкторі Coffee , то coffee.getSugar().getQuantity() поверне виняток NullPointerException . Звичайно, ми завжди можемо використовувати старі добрі перевірки null , щоб виправити проблему.
Coffee coffee = new Coffee();
Integer quantity = 0;
if (coffee.getSugar() != null) {
  quantity = coffee.getSugar().getQuantity();
}
Тепер начебто все нормально. Але при написанні Java-коду нам краще уникати реалізації перевірок null . Погляньмо, як це можна зробити, використовуючи Optional.

Як створити Optional

Існує три способи створення об'єктів Optional:
  • of(T value) - створення екземпляра Optional ненульового (non-null) об'єкта. Майте на увазі, що використання of() посилання на об'єкт null призведе до появи NullPointerException .

  • ofNullable(T value) - Створення значення Optional для об'єкта, який може бути нульовим (null).

  • empty() - створення екземпляра Optional, який представляє посилання на null .

// пример использования Optional.of(T Value)
String name = "foo";
Optional<String> stringExample = Optional.of(name)
// пример использования Optional.ofNullable(T Value)
Integer age = null;
Optional<Integer> integerExample= Optional.ofNullable(age)
// пример использования Optional.empty()
Optional<Object> emptyExample = Optional.empty();
Отже, у вас є об'єкт Optional. А тепер давайте познайомимося із двома основними методами для Optional:
  • isPresent() — цей метод повідомляє вам, чи містить об'єкт Optional ненульове (non-null) значення.

  • get() — витягує значення для Optional з поточним значенням. Майте на увазі, що виклик get() для порожнього Optional призведе до NullPointerException .

Врахуйте, що якщо ви використовуєте тільки get() і isPresent() при роботі з Optional, ви багато пропустите! Щоб це зрозуміти, давайте зараз перепишемо наведений вище приклад із Optional.

Поліпшення перевірки Null за допомогою Optional

Отже, як нам покращити наведений вище код? З Optional можна зрозуміти наявність об'єкта за допомогою isPresent() і отримати його за допомогою get() . Почнемо з упаковки результату coffee.getSugar() з Optional та використанням методу isPresent() . Це допоможе нам визначити, чи getSugar() повертає значення null.
Coffee coffee = new Coffee();
Optional<String> sugar = Optional.ofNullable(coffee.getSugar());
int quantity = 0;
if (sugar.isPresent()) {
  Sugar sugar = sugar.get();
  int quantity = sugar.getQuantity();
}
Дивлячись на цей приклад, упаковка результату coffee.getSugar() в Optional, схоже, не додає жодної цінності, а скоріше додає клопоту. Ми можемо покращити результат, використовуючи те, що я вважаю своїми улюбленими функціями з класу Optional:
  • map(Function<? super T,? extends U> mapper) - перетворює значення, що міститься в Optional, з наданою функцією. Якщо параметр Optional порожній, то map() поверне Optional.empty() .

  • orElse(T other) - це "спеціальна" версія методу get() . Вона може отримати значення, що міститься в Optional. Однак у разі порожнього Optional це поверне значення, передане в метод orElse() .

Метод поверне значення, що міститься в екземплярі Optional. Але якщо параметр Optional порожній, тобто він не містить значення, тоді абоElse() поверне значення, передане в сигнатуру його методу, яке відоме як значення за умовчанням.
Coffee coffee = new Coffee();

Integer quantity = Optional.ofNullable(coffee.getSugar())
    .map(it -> it.getQuantity())
    .orElse(0);
Це реально круто – принаймні я так думаю. Тепер, якщо у разі порожнього значення ми не хочемо повертати значення за замовчуванням, нам потрібно створити якийсь виняток. orElseThrow(Supplier<? extends X> exceptionSupplier) повертає значення, що міститься в параметрах Optional, або видає виняток у разі порожнього Optional.
Coffee coffee = new Coffee();

Integer quantity = Optional.ofNullable(coffee.getSugar())
  .map(it -> it.getQuantity())
  .orElseThrow(IllegalArgumentException::new);
Як бачите, Optional дає кілька переваг:
  • абстрагує перевірки null
  • надає API для обробки об'єктів null
  • дозволяє декларативному підходу висловлювати те, що досягається

Як стати ефективним з Optional

У своїй роботі я використовую Optional як тип, що повертається, коли метод може повертати стан “no result”. Зазвичай я використовую його при визначенні типів, що повертаються для методів.
Optional<Coffee> findByName(String name) {
   ...
}
Іноді в цьому немає потреби. Наприклад, якщо у мене є метод, що повертає int , наприклад getQuantity() в класі Sugar , тоді метод може повернути 0 якщо результат дорівнює null , щоб представити "no quantity" (немає кількості). Тепер, знаючи це, ми можемо подумати, що параметр Sugar у класі Coffee може бути представлений як Optional. На перший погляд це здається гарною ідеєю, оскільки теоретично цукор не обов'язково має бути присутнім у каві. Однак саме тут я хотів би зупинитися на тому, коли не слід використовувати Optional. Ми повинні уникати використання Optional у таких сценаріях:
  • Як типи параметрів для POJO , таких як об'єкти DTO . Optional не серіалізується, тому їх використання в POJO позбавляє об'єкт можливості серіалізації.

  • Як аргумент методу. Якщо аргумент методу може бути null , то з точки зору чистого коду передача null, як і раніше, краще, ніж передача Optional. Крім того, ви можете створити перевантажені методи абстрактної обробки відсутності аргументу нульового методу.

  • Для представлення об'єкта Collection, який немає. Колекції можуть бути порожніми, тому порожня Collection , як порожній Set або List , повинна використовуватися для подання Collection без значень.

Висновок

Optional став потужним доповненням до бібліотеки Java. Він дає спосіб обробки об'єктів, які можуть не існувати. Тому його слід враховувати під час розробки методів, але не потрапляючи у пастку зловживання. Так, ви можете написати відмінний код, який реалізує перевірки null та обробку null, але спільнота Java вважає за краще використовувати Optional. Він ефективно повідомляє, як обробляти відсутні значення, його набагато легше читати, ніж безладні перевірки Null, і в довгостроковій перспективі це призводить до меншої кількості помилок у коді.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ