Класс Optional в Java: что это и зачем он нужен - Вопросы
Всего: 5 вопросов
1. Что такое Optional в Java и в какой версии он появился?
Что такое Optional в Java и в какой версии он появился?
Optional<T> — это класс-контейнер из пакета java.util, который находится ровно в одном из двух состояний: либо содержит одно значение типа T (внутри всегда не null), либо пуст. Класс появился в Java 8 вместе с лямбда-выражениями. Он объявлен final и неизменяем: положить в существующий контейнер другое значение нельзя, любая «модификация» порождает новый объект. Смысл класса в том, чтобы возможное отсутствие значения было выражено в типе явно, а не пряталось за null: метод, возвращающий Optional<String>, уже своим объявлением предупреждает, что результата может не быть.
2. Почему у Optional нет публичных конструкторов и как тогда создать объект Optional?
Почему у Optional нет публичных конструкторов и как тогда создать объект Optional?
Конструкторы Optional объявлены с модификатором private, поэтому строка new Optional<>("текст") не компилируется. Это сделано намеренно: фабричные методы позволяют переиспользовать один общий объект для пустого состояния и гарантируют, что внутри непустого контейнера никогда не окажется null. Экземпляры создаются тремя статическими методами: Optional.empty() — пустой контейнер, Optional.of(value) — контейнер с заведомо не-null значением, Optional.ofNullable(value) — контейнер со значением или пустой, если аргумент null.
3. Чем Optional.of() отличается от Optional.ofNullable() и когда что использовать?
Чем Optional.of() отличается от Optional.ofNullable() и когда что использовать?
Разница в реакции на null. Optional.of(value) оборачивает значение и немедленно бросает NullPointerException, если аргумент равен null. Optional.ofNullable(value) в этом случае исключения не бросает: для не-null аргумента вернётся непустой контейнер, для null — Optional.empty(). Правило выбора: of() — когда null недопустим и означает ошибку в программе, которую надо обнаружить сразу; ofNullable() — когда null является штатным результатом: данные из чужого API, из базы, из кэша. Оборачивать в Optional.of() «на всякий случай», не будучи уверенным в аргументе, бессмысленно — вы просто перенесёте NullPointerException на строку выше.
4. Зачем возвращать Optional вместо null и где применять Optional уместно?
Зачем возвращать Optional вместо null и где применять Optional уместно?
Сигнатура Chapter getChapter(int number) ничего не говорит о том, вернёт ли метод null для несуществующей главы, — контракт спрятан в документации, и забытая проверка обернётся NullPointerException у пользователя. Сигнатура Optional<Chapter> getChapter(int number) заявляет возможное отсутствие результата прямо в типе: у Optional<Chapter> нет метода getSummary(), поэтому обратиться к значению случайно нельзя — контейнер придётся распаковать осознанно. Основной сценарий, ради которого класс создавался, — тип возвращаемого значения метода. Для полей и параметров Optional применяют реже: он не реализует Serializable, добавляет лишний объект на каждое поле и плохо дружит с фреймворками, отображающими поля на колонки БД; для необязательного параметра проще сделать перегрузку метода.
5. Гарантирует ли Optional, что NullPointerException больше не возникнет?
Гарантирует ли Optional, что NullPointerException больше не возникнет?
Нет. Сама переменная типа Optional может оказаться null, если ей присвоили null вместо Optional.empty(). Кроме того, вызов get() на пустом контейнере бросит NoSuchElementException — по сути ту же аварию под другим именем, а Optional.of(null) бросит NullPointerException в точке создания. Optional не убирает ошибки автоматически: он переводит отсутствие значения из неявного состояния в явное, делая необязательность результата видимой в типе. Отсюда и практические правила: метод, возвращающий Optional, никогда не возвращает null; для пустой коллекции используют Collections.emptyList(), а не Optional<List<String>>; сравнивают контейнеры через equals(), а не через ==.