что такое optional java

Класс Optional

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

Введен класс java.util.Optional начиная с версии Java 8.

Если кратко, то Optional либо хранит объект типа T, либо не хранит ничего (обертка над null). Далее мы приведем пример, показывающий, как плохо жить без Optional, и какие проблемы он решает.

Проблема с null

Давайте рассмотрим пример, демонстрирующий проблему. Допустим, у нас есть вложенная структура: Квартира (Apartment), Кухня (Kitchen), Холодильник (Fridge), Марка холодильника (brand). Марку холодильника мы получаем так:

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

Так что любой из этих геттеров может провалиться и завершить аварийно нашу программу.

Например, кухни нет, тогда getKitchen() возвращает null — это распространенная практика. Но тогда следующий за ним геттер getFridge() вызывается на объекте null, и программа завершается с NullPointerException.

Такие ситуации обходятся проверками:

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

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

возвращать из метода:

Теперь пара слов об аналогах, которые вдохновили инженеров Oracle написать класс Optional.

Что есть аналогичное в других языках

Проблема хранения и возврата отсутствующего значения существует и в других языках. Она как-то решается. На момент создания класса Optional мы имеем следующее.

Optional в Java 8

Итак, что же у нас в Java 8? Вернемся к примеру.

С Optional класс Apartment будет такой:

Класс Kitchen такой:

Глядя на эти классы, сразу понятно, что kitchen и fridge могут иметь, а могут не иметь значений (иначе говоря, быть null). Это упрощает чтение кода.

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

В итоге с Optional получение марки будет выглядеть так:

Теперь о том, как создавать объект типа Optional (хотя мы уже создавали его в методах выше).

Создание Optional

Optional — это обертка для объекта типа T. Мы создаем Optional, оборачивая обычный объект. Есть три метода обернуть объект: обернуть можно null, ненулевой объект и есть метод для оборачивания любого (как нулевого, так и ненулевого) объекта.

Этот метод возвращает Optional, который оборачивает нулевую кухню. Если же мы знаем, что кухня ненулевая, обернуть ее можно с помощью метода Optional.of():

Обратите внимание, что если в этот метод передать null, будет выброшено исключение:

Но обычно мы оборачиваем объект, про который неизвестно, null ли он, например, как в геттерах выше. Для этого используется метод Optional.ofNullable. Он не выбросит исключение, даже если передаваемый объект равен null:

Обратите внимание, мы использовали в тесте метод isPresent(), он возвращает true, если Optional содержит ненулевое значение.

Теперь рассмотрим некоторые типовые ситуации, когда Optional полезен.

Проверка на null — isPresent()

Часто в старом коде встречаются такие проверки:

С новым классом вышеприведенный код выполняется так:

Значение или действие по умолчанию — orElse(), orElseThrow()

Для проверки и присвоения значения по умолчанию использовался тернарный оператор:

С Optional это делается так:

Вот здесь происходит присваивание, поскольку maybeKitchen содержит не null:

А здесь будет выброшено исключение:

Сложная проверка — filter()

Ранее мы проверяли наличие значения с помощью ifPresent() и делали что-то. Допустим, надо выполнить более сложную проверку. Давайте напечатаем марку холодильника, причем только в том случае, если она равна «LG».

Метод filter() возвращает тот Optional, на котором вызван, если выполняется условие. Иначе он возвращает Optional.empty().

Извлечение и преобразование значений с помощью map()

Вышеприведенную задачу можно также решить с помощью метода map():

В map() передается функция преобразования, которая применяется к значению в Optional, если оно не null. Иначе возвращается Optional.empty(). Исключений не выбрасывается, независимо от того, null ли холодильник или нет.

Во второй строке мы делаем фильтрацию и печатаем значение.

flatMap() для цепочки Optional

Вернемся к нашей задаче — получить по цепочке кухню, холодильник и марку. Марку из холодильника мы получили в последнем примере с помощью map(). При этом избавились от проблемы NullPointerException.

Чтобы убрать вложенный Optional, придуман метод flatMap(). С его помощью мы можем вызвать цепочку геттеров:

Читайте также:  что значат значки в земли значат значки

На последнем звене цепочки мы вызываем map(), потому что getBrand() возвращает не Optional, а обычное значение — это было проделано уже в предыдущем разделе.

Для кухни же и квартиры мы используем flatMap().

Обратите внимание, что если нашу структуру значениями не заполнять, то никакого NullPointerException не будет (хотя в в старом коде исключение возникало). Здесь мы создаем Apartment, но никакого холодильника и кухни ей не назначаем, и все методы, тем не менее, вызываются безопасно:

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

При чтении кода с Optional сразу понятно, где значения не обязательны, и код гораздо короче.

Код вышеприведенных примеров доступен на GitHub.

Класс Optional: 2 комментария

А ничего, что flatmap принимает функциональный интерфейс function и передаваемая конструкция Apartment::getKitchen не совпадает по сигнатуре с методом apply интерфейса function?как ссылка на метод в таком случае передается?

Источник

Новый класс Optional в Java 8, не панацея от NullPointerException

Итак, приступим к описанию основных возможностей этого нововведения.

Создание объектов Optional

Для начала приведу пример класса с использованием Optional :

Использование Optional для устранения избыточного кода

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

А теперь все то же самое, но с использованием Optional :

Намного лаконичнее, не правда ли?

Действия с объектом, с использованием метода ifPresent()

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

Те же действия, но с использованием Optional :

Действия с объектом с использованием isPresent()

isPresent() не дает нам большой выгоды по устранению избыточности кода, но зато придает немного большую информативность написанному коду.

То же самое, но с использованием Optional :

Действия с объектом с использованием orElse(), orElseThrow()

И напоследок еще несколько методов для наведения «красоты» в коде.

То же самое, но с использованием Optional :

Или, если не хотим создавать объект, можно выбросить исключение:

Заключение

Естественно Optional не дает никакой гарантии избавления от NullPointerException и все проверки на null можно было описывать и раньше, но с Optional это действия становятся быстрее и проще, так как дополнительные методы для проверки объекта или проверки и каких-то дальнейших действий с объектом уже описаны и нам осталось только воспользоваться ими в своем коде.

А также несомненно, Optional помогает придать большую информативность коду, повысить его читабельность.

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

Источник

Java 8 Optional

Данная статья написана командой Vertex Academy. Это одна из статей из нашего Учебника по Java 8. Надеемся, что данная статья Вам будет полезна. Приятного прочтения!

В этой статье мы рассмотрим опциональные типы (Optional) в Java 8

1. Введение

Благодаря опциональным типам можно забыть про проверки на null и NullPointerException.

2. Optional basics

Для создания Optional используются методы:

Optional.of

В метод Optional.of нельзя передавать null, если конечно мы не хотим получить NullPointerException

— Optional.ofNullable

А вот в метод Optional.ofNullable передавать null можно безопасно

Optional.empty для создания пустого Optional

Для получения значения из Optional используется метод Optional.get, но он является небезопасным и может бросить NoSuchElementException

3. Optional isPresent, ifPresent

3.1 Optional isPresent

Метод Optional.isPresent возвращает true, если значение в нем присутствует, иначе возвращает false

Метод Optional.get лучше использовать в паре с Optional.isPresent чтобы предотвратить исключения

3.2 Optional ifPresent

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

4. Optional orElse

4.1 Optional orElse

Метод Optional.orElse возвращает переданное значение, если Optional пустой

4.2 Optional orElseGet

4.3 Optional orElseThrow

5. Optional map, flatMap

5.1 Optional map

Метод Optional.map служит для преобразования значения внутри Optional. Если Optional пустой преобразование не будет происходить

5.2 Optional flatMap

Метод Optional.flatMap преобразовывает значение внутри Optional, но при этом не оборачивает их

В Java 8 есть еще множество полезных нововведений, которые можно найти тут

Источник

Optional: Кот Шрёдингера в Java 8

Представим, что в коробке находятся кот, радиоактивное вещество и колба с синильной кислотой. Вещества так мало, что в течение часа может распасться только один атом. Если в течение часа он распадётся, считыватель разрядится, сработает реле, которое приведёт в действие молоток, который разобьёт колбу, и коту настанет карачун. Поскольку атом может распасться, а может и не распасться, мы не знаем, жив ли кот или уже нет, поэтому он одновременно и жив, и мёртв. Таков мысленный эксперимент, именуемый «Кот Шрёдингера».

Читайте также:  что такое arx control

Класс Optional обладает схожими свойствами — при написании кода разработчик часто не может знать — будет ли существовать нужный объект на момент исполнения программы или нет, и в таких случаях приходится делать проверки на null. Если такими проверками пренебречь, то рано или поздно (обычно рано) Ваша программа рухнет с NullPointerException.

Коллеги! Статья, как и любая другая, не идеальна и может быть поправлена. Если Вы видите возможность существенного улучшения данного материала, укажите её в комментариях.

Как получить объект через Optional?

Как уже было сказано, класс Optional может содержать объект, а может содержать null. К примеру, попытаемся извлечь из репозитория юзера с заданным ID:

Возможно, юзер по такому ID есть в репозитории, а возможно, нет. Если такого юзера нет, к нам в стектрейс прилетает NullPointerException. Не имей мы в запасе класса Optional, нам пришлось бы изобретать какую-нибудь такую конструкцию:

Согласитесь, не очень. Намного приятнее иметь дело с такой строчкой:

Мы получаем объект, в котором может быть запрашиваемый объект — а может быть null. Но с Optional надо как-то работать дальше, нам нужна сущность, которую он содержит (или не содержит).

Cуществует всего три категории Optional:

.ifPresent()

Метод позволяет выполнить какое-то действие, если объект не пустой.

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

.isPresent()

Этот метод возвращает ответ, существует ли искомый объект или нет, в виде Boolean:

Если Вы решили использовать нижеописанный метод get(), то не будет лишним проверить, существует ли данный объект, при помощи этого метода, например:

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

Как получить объект, содержащийся в Optional?

Существует три прямых метода дальнейшего получения объекта семейства orElse(); Как следует из перевода, эти методы срабатывают в том случае, если объекта в полученном Optional не нашлось.

.orElse()

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

Эта конструкция гарантированно вернёт нам объект класса User. Она очень выручает на начальных этапах познания Optional, а также, во многих случаях, связанных с использованием Spring Data JPA (там большинство классов семейства find возвращает именно Optional).

.orElseThrow()

Очень часто, и опять же, в случае с использованием Spring Data JPA, нам требуется явно заявить, что такого объекта нет, например, когда речь идёт о сущности в репозитории. В таком случае, мы можем получить объект или, если его нет, выбросить исключение:

Если сущность не обнаружена и объект null, будет выброшено исключение NoEntityException (в моём случае, кастомное). В моём случае, на клиент уходит строчка «Пользователь не найден. Проверьте данные запроса».

.orElseGet()

Если объект не найден, Optional оставляет пространство для «Варианта Б» — Вы можете выполнить другой метод, например:

Если объект не был найден, предлагается поискать в другом месте.

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

Работа с полученным объектом.

Как я писал выше, у Optional имеется неплохой инструментарий преобразования полученного объекта, а именно:

Метод get() возвращает объект, запакованный в Optional. Например:

Этот метод полностью повторяет аналогичный метод для stream(), но срабатывает только в том случае, если в Optional есть не-нулловый объект.

В примере мы получили одно из полей класса User, упакованного в Optional.

.filter()

Данный метод также позаимствован из stream() и фильтрует элементы по условию.

.flatMap()

Этот метод делает ровно то же, что и стримовский, с той лишь разницей, что он работает только в том случае, если значение не null.

Источник

Java Optional не такой уж очевидный

Optional не должен равняться null

Знайте API

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

Идея проста: если имя отсутствует, вернуть значение по умолчанию. Можно сделать это лучше.

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

Optional.filter упрощает код.

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

Отдавайте предпочтение контейнерам «на примитивах»

Не пренебрегайте ленивыми вычислениями

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

Читайте также:  что такое автоподача в принтере

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

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

Не оборачивайте коллекции в Optional

Хотя я и видел такое не часто, иногда это происходит.

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

Чрезмерное использование Optional усложняет работу с API.

Не передавайте Optional в качестве параметра

А сейчас мы начинаем обсуждать наиболее спорные моменты.

Во-вторых, applySettings обладает четырьмя потенциальными поведениями в зависимости от того, являются ли переданные Optional пустыми, или нет.

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

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

Что же, как мы можем улучшить этот код? Если age и role должны всегда присутствовать, мы можем легко избавиться от Optional и решать проблему отсутствующих значений на верхнем уровне.

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

С другой стороны, если значения age и role могут быть опущены, вышеописанный способ не заработает. В этом случае лучшим решением будет разделение API на отдельные методы, удовлетворяющим разным пользовательским потребностям.

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

Не используйте Optional в качестве полей класса

Я слышал разные мнения по этому вопросу. Некоторые считают, что хранение Optional напрямую в полях класса позволяет сократить NullPointerException на порядок. Мой друг, который работает в одном известном стартапе, говорит, что такой подход в их компании является утвержденным паттерном.

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

Отсутствие сериализуемости

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

Хранение лишних ссылок

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

Плохая интеграция со Spring Data/Hibernate

Предположим, что мы хотим построить простое Spring Boot приложение. Нам нужно получить данные из таблицы в БД. Сделать это очень просто, объявив Hibernate сущность и соответствующий репозиторий.

Теперь все сломано.

Hibernate не может замапить значения из БД на Optional напрямую (по крайней мере, без кастомных конвертеров).

Но некоторые вещи работают правильно

Должен признать, что в конечном итоге не все так плохо. Некоторые фреймворки корректно интегрируют Optional в свою экосистему.

Jackson

Давайте объявим простой эндпойнт и DTO.

Как ни странно, все по-прежнему работает так, как и ожидается.

Это значит, что мы можем использовать Optional в качестве полей DTO и безопасно интегрироваться со Spring Web? Ну, вроде того. Однако есть потенциальные проблемы.

SpringDoc

SpringDoc — это библиотека для Spring Boot приложений, которая позволяет автоматически сгенерировать Open Api спецификацию.

Вот пример того, что мы получим для эндпойнта GET /person/ .

Это приведет к интересным результатам.

В чем здесь проблема? Например, если кто-то на фронтенде использует генератор типов сущностей по схеме Open Api, это приведет к получению неверной структуры, что в свою очередь может привести к повреждению данных.

Решение

Что же нам делать со всем этим? Ответ прост. Используйте Optional только для геттеров.

Однако у этого подхода есть один недостаток. Его нельзя полностью интегрировать с Lombok. Optional getters не подерживаются библиотекой и, судя по некоторым обсуждениям на Github, не будут.

На текущий момент единственным выходом является ручное объявление необходимых геттеров.

Источник

Строительный портал