Класс Optional появился в Java 8. Задачей этого класса является предоставление решений на уровне типа-обертки для обеспечения удобства обработки возможных null-значений. Звучит не слишком просто. Наглядно это можно представить так: Хорошее представление класса Optional есть в статье на сайте Oracle. Существует несколько способов. Чтобы создать пустой объект можно использовать статический метод empty(): Метод isPresent() проверяет наличие значения внутри объекта Optional. Чуть позже мы вернемся к этому методу. Также объект Optional можно создать с помощью статического метода of(): В этом варианте объект, который мы передаем в метод of(), должен быть не null. Иначе получим NullPointerException: Если мы предполагаем, что передаваемое значение может быть null, то используем другой метод - ofNullable(): Если мы передадим в этот метод null, то вместо NPE получим просто пустой объект Optional: Когда у нас имеется объект Optional, который мы получаем из метода или создаем сами, с помощью метода isPresent() можно проверить есть ли что-то внутри: Метод возвращает true, если оборачиваемое значение не null. С Java 11 появился метод isEmpty(), который делает ровно противоположное: Метод ifPresent() позволяет нам выполнить код над объектом внутри Optional, если он не null. Раньше код был такой: Перед тем как выполнить метод, нам необходимо проверить значение на null. Это долго и может привести к ошибкам в будущем. Где гарантия, что далее в коде мы не будем использовать эту переменную и не забудем сделать проверку на null потом?  Тогда, во время выполнения, программа рано или поздно упадет с NullPointerException. Класс Optional позволяет нам лучше и качественнее работать с null-значениями. Посмотрим как можно улучшить этот кусочек кода и перепишем его в функциональном стиле: 1 строчка - оборачиваем объект в Optional 2 строчка - валидация значения и исполнение кода Метод orElse() используется для получения значения, которое находится внутри Optional. Он принимает один параметр - значение по умолчанию. Таким образом orElse()  возвращает или само значение из Optional или переданный аргумент: Метод orElseGet() очень похож на метод orElse(). Однако, вместо значения по умолчанию, которое передается в качестве аргумента, метод принимает функциональный интерфейс Supplier, который вызывает определенное действие. Посмотрим на пример: В примере для наглядности мы создаем анонимный класс. Поскольку мы находимся в парадигме функционального программирования, код можно упростить, используя лямбду: На первый взгляд, разницы нет. По крайней мере, создается такое впечатление, не так ли? Тем не менее, методы отличаются. Давайте попробуем это понять на примерах. Создадим метод, который ничего не принимает и возвращает значение по умолчанию: Напишем теперь два теста с использованием методов orElseGet() и orElse(): В этом примере мы оборачиваем null-объект в Optional и пытаемся получить его значение с orElseGet() и orElse(). В обоих случаях "побочный" эффект одинаков: Метод getMyDefault() вызывается и для orElseGet(), и для orElse(). Когда значение внутри Optional отсутствует, методы работают идентично. Теперь напишем еще один тест, в котором оборачиваемый объект будет не null и в котором значение по умолчанию, в идеале, не должно создаваться. А теперь заглянем в консоль: Обратите внимание, что в случае метода orElseGet() метод getMyDefault() даже не вызывается. Но в случае использования метода orElse() объект по умолчанию создается и он ни разу даже не используется. В этом простом примере создание объекта по умолчанию не является для нас чем-то "дорогим", но что если метод getMyDefault() должен выполнить какой-то тяжелый и "дорогостоящий" запрос к БД или другому сервису? Метод orElseThrow() вместо предоставления дефолтного метода выбрасывает исключение: С Java 10 orElseThrow() поддерживает пустой конструктор. В случае, если Optional содержит null, выбрасывается NoSuchElementException.   Еще один способ получить значение из Optional - вызвать метод get(): В отличие от предыдущих способов get() возвращает значение только если оно не null. В противном случае выбрасывается NoSuchElementException.   Метод filter() принимает предикат в качестве аргумента и возвращает объект Optional. Если значение проходит условие, заданное предикатом, то Optional возвращается без изменений. Если предикат возвращает false, то на выходе мы получим пустой Optional: Метод filter() используется для отбраковки обернутых значений по заранее определенным правилам. Давайте еще посмотрим на один пример. Мы хотим купить наушники и нас интересует только их цена. Мы получаем push-уведомления о ценах на наушники с определенного сайта и храним их в объектах:  Теперь мы передаем этот объект какому-то коду, единственная цель которого проверить, подходит ли он нам по цене. Как это будет без Optional: Посмотрите сколько кода...  Значимых строчек только две - 5 и 6, остальные проверочные  Теперь попробуем про все это забыть и сделать с помощью Optional и его метода filter(): Метод map() используется для преобразования одного значения в другое. Он не меняет оригинального значения. В итоге мы получаем optional-объект цены. Во-первых, нас теперь больше не волнует возможное появление null-значений. Во-вторых, нам пришлось писать только указанную в названии метода логику - проверять цену. Все остальное за нас делает Optional. Согласитесь, решение с Optional в данном примере намного более компактное и избавляет нас от лишних проверок, не связанных с основной логикой.  В прошлом разделе мы увидели как проверить значение на соответствие некоторым условиям. С помощью метода map() можно изменить и само значение, и его тип. Действие, которое мы выполняем, заключается в получении значения размера списка элементов из самого списка. Метод map() возвращает результат вычислений, завернутый внутрь Optional. Затем нам нужно вызвать соответствующий метод, чтобы получить его значение. Обратите внимание на разницу. Метод filter() просто проверяет значение и возвращает Optional с объектом того же типа, если оно соответствует заданному предикату. В противном случае возвращается пустой Optional. Метод map() берет существующее значение, выполняет вычисления и возвращает их результат, обернутый в объект Optional: Можно комбинировать методы map() и filter() в цепочки, чтобы сделать какую-то более сложную операцию. В следующем примере мы сначала очищаем пароль от лишних символов с помощью метода map() и только потом делаем проверку filter(): Без предварительной "очистки" наш пароль пройдет сквозь метод фильтра (первый assert). Разница между map() и flatMap() в том, что map() изменяет только "распакованные" значения, а flatMap() перед изменением "распаковывает" значение самостоятельно. Чтобы это лучше понять, давайте сразу посмотрим на пример. Возьмем произвольный объект Person c полями имя, возраст и пароль: Представим, что нам вернулся объект Person обернутый в Optional (или же мы сами его таким создали): Теперь если мы попробуем получить поля этого объекта, они тоже будут будут обернуты в Optional: Обратите внимание, что в этом примере мы получаем доступ к полю объекта двумя путями: с помощью map() и flatMap(). Использование map() оборачивает в Optional то, что мы получаем вызовом getName. В итоге нам приходится делать дополнительную "распаковку". FlatMap() же делает это неявно за нас.  Иногда нам может понадобиться получить первый непустой объект Optional из нескольких. Давайте сразу обратимся к примеру и посмотрим на методы, которые будем использовать далее:  Используем Stream API, чтобы сформировать цепочку Optional и вернуть первый непустой элемент: Недостатком такого метода является то, что все наши get-методы всегда исполняются, независимо от того где мы встретим затем в потоке непустой объект. Если мы хотим воспользоваться "ленивой" обработкой, нам нужно использовать ссылку на метод и интерфейс Supplier: Если нам необходимо использовать методы, принимающие аргументы, то на помощь придут лямбда-выражения: Иногда нам необходимо вернуть какое-то значение по умолчанию, если все Optional пустые. Мы можем сделать это, просто добавив вызов orElse() или orElseGet(): Начиная с 9-ки в Optional добавилось несколько новых методов: метод or() с помощью Supplier создает альтернативный Optional; метод ifPresentOrElse() позволяет выполнить действие, если Optional не пустой, и другое действие, если пустой; метод stream() преобразует Optional в поток. Представьте, что у нас есть лист Person и мы хотим использовать метод, который будет искать людей по имени. Кроме этого мы хотим, чтобы можно было искать и по возрасту, если он указан. Получится такой код: Вызовем метод: И получим NullPointerException. В итоге мы вынуждены будем проверять наш параметр на null, что противоречит нашей первоначальной задаче избежать этих проверок. Как можно это исправить? Давайте глянем: Еще один вариант - создать перегруженные методы: Существуют решения, позволяющие избежать использования Optional в качестве параметров метода. Запомните, что Optional появилось в Java, чтобы его использовали в качестве возвращаемого типа, таким образом указывая, что метод может вернуть пустое значение. Несмотря на большое количество возможностей, предоставляемых классом Optional, злоупотреблять ими тоже не стоит. Старайтесь избегать ситуаций с возвратом null из методов. Большинство клиентов вашего API ожидают получать безопасный ответ. Возврат null может привести к NullPointerException. Вместо этого можно вернуть Optional.empty. Старайтесь не использовать Optional в качестве параметра в методах. Если такое происходит, то придется делать лишнюю обертку для аргумента. Это ухудшит читаемость кода. Если параметра в методе может не быть, то лучше использовать перегруженную версию метода. В большинстве ситуаций удобнее использовать Optional.map и Optional.flatMap вместо Optional.ifPresent, чтобы избежать лишних проверок. Optional не имплементирует интерфейс Serializable, поэтому лучше не использовать его в качестве полей класса. Основная его цель - использоваться в качестве возвращаемого значения, если такое значение может отсутствовать. Не злоупотребляйте использованием Optional. Я начинающий Java-разработчик. В рамках развития своей карьеры я решил сделать две вещи: Завести канал в ТГ, где буду рассказывать о себе, своем пути и проблемах, с которыми сталкиваюсь - https://t.me/java_wine Завести блог на Хабре, куда буду выкладывать материалы, которые использую для своего обучения и развития, которые лично мне помогли разобраться с той или иной темой. Надеюсь, буду полезен сообществу и новичкам, которые пойдут по моим или чьим-то еще стопам. Спасибо!