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"]
🎤 Закрыл тему, если можешь объяснить:
• зачем Optional и чем он лучше чем просто null;
• разницу orElse и orElseGet, и где Optional использовать не стоит (если дошёл до 2-го слоя).