Java Optional — Отец холиваров

java8-optional

  1. Что такое Optional, почему он полезен? ->
  2. Как использовать Optional ->
  3. Использование, уместное и не очень ->
  4. Холивары ->
  5. Итоги ->


Что такое Optional, почему он полезен?

  • Optional<T> был введен в Java 8
  • Может находиться в 2ух состояниях
    • Содержит ссылку на T (также «present» или присутствует)
    • Пуст (также «absent» или отсутствует.  Не употребляйте «null»)
  • Реализации для примитивных типов
    • OptionalInt, OptionalLong, OptionalDoube
  • Optional — ссылочный тип, он может быть null (! Никогда так не делайте)

Правило #1: Никогда не используйте null как значение Optional или в качестве возвращаемого значения

 

Если нам нужно получить не клиента, а его имя?

Итак:

Optional — это ограничительный механизм для методов возвращаемые значения которых требуют отдавать некоторый «no result», представленный  null, который может вызвать дальнейшие ошибки

Как использовать Optional

Рассмотрим пример с использованием Optional

Правило #2: Никогда не используйте Optional.get() без предварительной проверки Optional.isPresent()

Правило #3: Выбирайте отличные способы работы с Optional, чем проверка на isPresent + get()

Как же тогда быть?

 

Дополнительные методы

  • Статичные фабричные
    • Optional.empty() — создает пустой Optional
    • Optional.of(T) — создает Optional T (T не может быть null)
  • flatMap(Function<T, Optional<U>>)
    • Работает как map(), но использует для трансформации функцию, возвращающую Optional
  • Optional.equals() и hashCode()

 

Адаптер null-optional

  • Есть nullable значение, нужен Optional
    • Optional<T> opt = Optional.ofNullable(ref);
  • Есть Optional, нужен nullable
    • opt.orElse(null); ! В других случаях избегайте этого

Использование, уместное и не очень.

Рассмотрим на примерах

Правило #4: В целом, создавать Optional только для того что бы получить из него значения — плохая идея. Лучше использовать тренарный оператор «?»

Правило #5: Если есть вложенная цепочка Optional или промежуточный результат Optional<Optional<T>>, вероятно, это излишне

 

Этот проблемный метод get()

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

  • Метод get() —  «attractive nuisance» или «заманчивая неприятность»
    • не сильно полезен
    • легко забыть обезопасить вызов
    • легко сбивает с толку стиль isPresent() + get()
    • неправильно используется в большом количестве случаев => Не совсем удачное API
  • Планы по ликвидации проблемы. (Возможно будет уже в Java9?)
    • представить замену get()
    • @Deprecated для get()

 

Правило #6: Не используйте Optional в полях объекта, параметрах методов и коллекциях

  • Помните, Optional это box!
    • требует 16 байт
    • это отдельный объект (создает нагрузку на GC)
    • Одиночный Optional — не проблема, но если в вашей структуре данных их много, это может привести к проблемам с производительностью
  • Не нужно пытаться заменить каждый null на Optional
    • null — безопасен, если хорошо контролируется
    • null в private поле легко проверяется
    • null в параметрах — это не плохо ( кончено, если код содержит предпроверки )

Холивары

  • Optional должен иметь возможность быть «present» со значением null!
  • Optional не защищает от всех NPE, поэтому он бесполезен
  • Optional должен быть serializable!
  • Optional должен быть частью языка, а не частью вспомогательной библиотеки!
  • Optional не должен быть final!
  • Optional должен расширять интерфейс Iterable, что позволит использовать его в циклах for!
  • У Optional должны быть дочерние классы Present и Empty!
  • Optional.ifPresent() должен возвращать «this» вместо void. Что бы использовать его в цепочках!
  • Нужно добавит в Java @Nullable / @NonNull вместо Optional!

Итоги

Новые методы Optional в Java9

  • Stream<T> Optional.stream()
  • void Optional.ifPresentOrElse(Consumer<T>, Runnable)
  • Optional<T> Optional.or(Supplier<Optional<T>>)

 

Правила

  1. Никогда не используйте null как значение Optional или в качестве возвращаемого значения
  2. Никогда не используйте Optional.get() без предварительной проверки Optional.isPresent()
  3. Выбирайте отличные способы работы с Optional, чем проверка на isPresent + get()
  4. В целом, создавать Optional только для того что бы получить из него значения — плохая идея. Лучше использовать тренарный оператор «?»
  5. Если есть вложенная цепочка Optional или промежуточный результат Optional<Optional<T>>, вероятно, это излишне
  6. Не используйте Optional в полях объекта, параметрах методов и коллекциях

 

Добавлю немного от себя:

Optional, наряду с stream API, и lambda — удобная фича языка, позволяющая улучшить читаемость кода, оптимизировать проверки, избежать не обрабатываемых exception. Главное правило — не забывать, что код нужно писать так, что бы коллега мог легко понять что тут происходит, и открыв его через пару месяцев после написания не пришлось заново разбирать всю логику :)

Код ради кода — не есть это хорошо

С большой силой приходит большая ответственность

 


Оригинал: Optional – The Mother of all Bikesheds


3 Комментария

Добавьте свой →

  1. Интересная на самом деле статья, т. к. не давно у нас на проекте появилассь поддержка java 8 и все принялись писать код изпользуя Optional, не изучив толком как работает данный класс.
    У меня есть пример где применял этот класс в рамках учебной практики по учебнику Head First https://github.com/turbomann/HF/blob/master/src/main/java/trx0eth7/chapter1/BeerSong.java

    • Да,он так и манит написать Optional.ofNullable(someVar).orElse(null) :)
      Еще не начали повально циклы на stream`ы переписывать?

      • Так и есть) И это приводит порой к нечитаемому виду. Если полезть то как реализованы stream`ы, то по сути это такой же код, который мы писали до.
        А вообще спасибо за статью.

Добавить комментарий