Optional
Optional<T> — обёртка, которая явно говорит: «здесь может не быть значения». До Java 8 методы возвращали null когда значения нет, и это приводило к NullPointerException — самому частому исключению в Java. Optional делает отсутствие значения явным на уровне типа.
// Старый способ — null и надежда на лучшее
String name = findUser(id).getName(); // NPE если findUser вернул null
// С Optional — явно говорим «может не быть»
Optional<User> user = findUser(id);
String name = user.map(User::getName).orElse("Гость");
Основные методы:
Optional<String> opt = Optional.of("Java"); // точно есть
Optional<String> empty = Optional.empty(); // точно нет
Optional<String> maybe = Optional.ofNullable(s); // может быть null
opt.isPresent() // true если есть значение
opt.get() // получить значение (кинет если пусто — не злоупотреблять)
opt.orElse("default") // значение или дефолт
opt.orElseGet(() -> computeDefault()) // дефолт вычисляется лениво
opt.orElseThrow() // значение или NoSuchElementException
opt.map(String::length) // преобразовать если есть → Optional<Integer>
opt.filter(s -> s.length() > 3) // отфильтровать → Optional
opt.ifPresent(System.out::println) // выполнить если есть
Копнуть глубже
Когда использовать Optional, а когда нет.
✅ Возвращаемый тип метода, когда значение может отсутствовать:
Optional<User> findById(Long id) { ... }
❌ Поле класса — Optional не Serializable, и это ломает JPA/Jackson:
// Плохо
private Optional<String> nickname; // не делать так
❌ Параметр метода — лучше две перегрузки или @Nullable:
// Плохо
void process(Optional<String> name) { ... }
❌ Коллекции — пустая коллекция лучше Optional<List>:
// Плохо
Optional<List<User>> getUsers() { ... }
// Хорошо
List<User> getUsers() { return Collections.emptyList(); }
orElse vs orElseGet — важное отличие. orElse(expr) вычисляет выражение всегда, даже если значение есть. orElseGet(() -> expr) — только если значение отсутствует. Если дефолт дорого вычислять (запрос к БД, создание объекта), всегда используйте orElseGet.
// orElse — createDefault() вызовется ВСЕГДА
opt.orElse(createDefault());
// orElseGet — createDefault() только если opt пуст
opt.orElseGet(() -> createDefault());
Optional в Stream API. С Java 9 у Optional есть метод stream() — превращает Optional в стрим из 0 или 1 элемента. Удобно для flatMap:
List<Optional<String>> opts = List.of(Optional.of("a"), Optional.empty(), Optional.of("b"));
List<String> values = opts.stream()
.flatMap(Optional::stream) // убрать пустые Optional
.toList(); // ["a", "b"]
• разницу orElse и orElseGet, и где Optional использовать не стоит (если дошёл до 2-го слоя).