Зачем Optional
Optional<T> — контейнер, который либо содержит значение, либо пуст. Его смысл — сделать «значения может не быть» ЯВНЫМ в типе возвращаемого значения, чтобы вызывающий не забыл про этот случай, как это происходит с молчаливым null.
Главное применение — тип результата методов, которые могут не найти значение (findById, findFirst). Это не универсальная замена null везде.
Создание и распаковка
Создают через Optional.of(x) (x не null, иначе NPE), Optional.ofNullable(maybeNull) и Optional.empty(). Достают значение безопасно: orElse(default), orElseGet(supplier), orElseThrow(), ifPresent(consumer), ifPresentOrElse.
Ключевая ловушка — orElse против orElseGet: аргумент orElse(expr) вычисляется ВСЕГДА, даже если Optional не пуст; orElseGet(() -> expr) вызывает поставщик ТОЛЬКО когда значения нет. Если дефолт дорогой или с побочным эффектом (запрос в БД, new) — это разница между лишней работой и ленивым вычислением.
// orElse всегда выполнит expensiveDefault(), даже если value присутствует:
String a = opt.orElse(expensiveDefault());
// orElseGet выполнит лишь при пустом opt:
String b = opt.orElseGet(() -> expensiveDefault());
Композиция
Optional поддерживает map (преобразовать значение, если есть), flatMap (когда функция сама возвращает Optional — разворачивает Optional<Optional<>>), filter (оставить, если подходит). Это позволяет строить цепочки без ручных проверок на null.
Например: findUser(id).map(User::address).map(Address::city).orElse("unknown") — безопасный путь сквозь возможные пропуски.
Антипаттерны
opt.get() без проверки — почти всегда ошибка (бросит NoSuchElementException); вместо isPresent()+get() используют map/orElse/ifPresent. Optional как ПОЛЕ класса или ПАРАМЕТР метода — антипаттерн (он не Serializable, добавляет обёртку и аллокацию; для полей берут обычный nullable + контроль доступа, для параметров — перегрузку или nullable).
Не возвращайте Optional<Collection> — верните пустую коллекцию (пустота уже выражает «ничего нет»). И не оборачивайте в Optional то, что не бывает пустым.
Как отвечать на собеседовании
Объясните цель Optional (явное отсутствие в типе результата, против тихого null), разберите orElse vs orElseGet (orElse вычисляет всегда — ленивость важна для дорогих дефолтов), покажите композицию map/flatMap/filter и назовите антипаттерны (get без проверки, Optional как поле/параметр, Optional<Collection>).