Optional

java.util.Optional<T> — контейнер, который может содержать значение или быть пустым. Это явный способ сказать «значение может отсутствовать», не прибегая к null. Optional особенно полезен как возвращаемое значение методов, которые «иногда не находят» результат: поиск в коллекции, чтение настроек, парсинг.

Идея простая: вместо того чтобы возвращать null и заставлять вызывающий код помнить про проверку, метод возвращает Optional. Тогда компилятор и API сами «подталкивают» программиста обработать оба случая.

Основные методы

Optional.of(value)         // value != null, иначе NPE
Optional.ofNullable(value) // допускает null -> empty
Optional.empty()

isPresent() / isEmpty()
get()                      // выбрасывает NoSuchElementException, если пусто
ifPresent(Consumer)
orElse(other)
orElseGet(Supplier)
orElseThrow(SupplierOfException)
map(Function)              // преобразование, если значение есть
flatMap(Function)          // если функция сама возвращает Optional
filter(Predicate)

Примеры

Пример 1. Создание Optional

import java.util.Optional;

public class OptionalCreate {
    public static void main(String[] args) {
        Optional<String> a = Optional.of("Робот");
        Optional<String> b = Optional.ofNullable(null);
        Optional<String> c = Optional.empty();

        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }
}

Output:

Optional[Робот]
Optional.empty
Optional.empty

Пример 2. ifPresent и orElse

import java.util.Optional;

public class OptionalIfPresent {
    public static void main(String[] args) {
        Optional<String> name = Optional.of("Phobo");
        name.ifPresent(n -> System.out.println("Имя: " + n));

        Optional<String> missing = Optional.empty();
        String fallback = missing.orElse("неизвестно");
        System.out.println("Значение: " + fallback);
    }
}

Output:

Имя: Phobo
Значение: неизвестно

Пример 3. map и filter

import java.util.Optional;

public class OptionalMap {
    public static void main(String[] args) {
        Optional<String> raw = Optional.of("  25  ");
        int distance = raw.map(String::trim)
                          .map(Integer::parseInt)
                          .filter(d -> d > 0)
                          .orElse(-1);
        System.out.println("Расстояние: " + distance);

        int bad = Optional.<String>empty()
                          .map(Integer::parseInt)
                          .orElse(-1);
        System.out.println("Из пустого: " + bad);
    }
}

Output:

Расстояние: 25
Из пустого: -1

Пример 4. orElseThrow

import java.util.Optional;

public class OptionalThrow {
    static String configValue(String key) {
        return null; // имитация отсутствия
    }

    public static void main(String[] args) {
        try {
            String value = Optional.ofNullable(configValue("PIN"))
                    .orElseThrow(() -> new IllegalStateException("PIN не задан"));
            System.out.println(value);
        } catch (IllegalStateException e) {
            System.out.println("Ошибка: " + e.getMessage());
        }
    }
}

Output:

Ошибка: PIN не задан

Пример 5. Поиск в коллекции

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class OptionalFind {
    public static void main(String[] args) {
        List<String> sensors = Arrays.asList("ИК", "УЗ", "Линия");

        Optional<String> found = sensors.stream()
                .filter(s -> s.startsWith("У"))
                .findFirst();

        System.out.println("Найдено: " + found.orElse("ничего"));

        Optional<String> missing = sensors.stream()
                .filter(s -> s.equals("GPS"))
                .findFirst();
        System.out.println("GPS: " + missing.orElse("нет такого"));
    }
}

Output:

Найдено: УЗ
GPS: нет такого

Пример 6. flatMap при вложенных Optional

import java.util.Optional;

public class OptionalFlatMap {
    static Optional<String> findUser(int id) {
        return id == 1 ? Optional.of("Айтен") : Optional.empty();
    }
    static Optional<String> findEmail(String user) {
        return user.equals("Айтен") ? Optional.of("aiten@example.com") : Optional.empty();
    }

    public static void main(String[] args) {
        String email = findUser(1)
                .flatMap(OptionalFlatMap::findEmail)
                .orElse("не найден");
        System.out.println(email);

        String none = findUser(2)
                .flatMap(OptionalFlatMap::findEmail)
                .orElse("не найден");
        System.out.println(none);
    }
}

Output:

aiten@example.com
не найден

Подводные камни

Предупреждение

  • Не вызывайте get() без предварительной проверки — это та же NullPointerException, только в другой обёртке.

  • Не используйте Optional для полей классов и параметров методов — по официальной рекомендации это тип для возвращаемого значения.

  • Optional.of(null) бросит NullPointerException. Если значение может быть null, берите Optional.ofNullable.

  • orElse(x) всегда вычисляет x, даже если значение есть. Если x — дорогой вызов, используйте orElseGet(() -> x).

  • Optional не предназначен для сериализации — он не Serializable.

См. также

Примечание

Материал подготовлен по мотивам документации классов java.util.Optional и java.util.stream и распространяется в рамках Oracle Free Documentation License. Текст написан своими словами, примеры кода — оригинальные.