Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Optional

Метод поиска не всегда что-то находит. Если в таком случае вернуть null, вызывающий код легко забудет проверку и упадёт с NullPointerException. Optional — это контейнер, который либо содержит значение, либо пуст, и сам тип напоминает: «результата может не быть».

Создание Optional

Optional.of() оборачивает существующее значение, Optional.empty() создаёт пустой контейнер. Методы isPresent() и isEmpty() проверяют, есть ли внутри значение:

void main() {
    Optional<String> name = Optional.of("Анна");
    Optional<String> nothing = Optional.empty();
 
    IO.println(name.isPresent());
    IO.println(nothing.isEmpty());
    IO.println(name);
    IO.println(nothing);
}

Результат:

true
true
Optional[Анна]
Optional.empty

Метод, который может не найти

Главное применение Optional — тип результата метода поиска. Если значение может оказаться null, его оборачивают через Optional.ofNullable():

Map<String, String> phones = Map.of("Анна", "+49 30 1234567");
 
Optional<String> findPhone(String name) {
    return Optional.ofNullable(phones.get(name));
}
 
void main() {
    IO.println(findPhone("Анна"));
    IO.println(findPhone("Олег"));
}

Результат:

Optional[+49 30 1234567]
Optional.empty

Тот, кто вызывает findPhone(), видит по типу, что телефона может не быть, и обязан решить, что делать в этом случае.

Значение по умолчанию

orElse() возвращает значение из контейнера или запасное, если контейнер пуст. orElseGet() делает то же, но вычисляет запасное значение лямбдой — только когда оно действительно нужно:

void main() {
    Optional<String> city = Optional.empty();
    IO.println(city.orElse("Город не указан"));
    IO.println(city.orElseGet(() -> "Берлин"));
}

Результат:

Город не указан
Берлин

Преобразование: map и filter

map() применяет функцию к значению, если оно есть, и возвращает новый Optional. filter() оставляет значение, только если оно подходит под условие. Пустой Optional проходит через них без изменений:

void main() {
    Optional<String> email = Optional.of("  Anna@Mail.com ");
 
    String result = email
            .map(String::strip)
            .map(String::toLowerCase)
            .filter(e -> e.contains("@"))
            .orElse("нет адреса");
    IO.println(result);
}

Результат:

anna@mail.com

Действие, если значение есть

ifPresent() выполняет действие только при наличии значения, а ifPresentOrElse() позволяет описать и запасную ветку. orElseThrow() возвращает значение или выбрасывает исключение, если его нет:

void main() {
    Optional<Integer> discount = Optional.of(10);
    discount.ifPresent(d -> IO.println("Скидка " + d + "%"));
 
    Optional<Integer> none = Optional.empty();
    none.ifPresentOrElse(
            d -> IO.println("Скидка " + d + "%"),
            () -> IO.println("Скидки нет"));
}

Результат:

Скидка 10%
Скидки нет

Частая ошибка

Метод get() возвращает значение, но у пустого Optional выбрасывает исключение. Вызывать его без проверки — та же ошибка, что и обращение к null:

void main() {
    Optional<String> phone = Optional.empty();
    IO.println(phone.get());
}

Результат:

Exception in thread "main" java.util.NoSuchElementException: No value present

Вместо get() используйте orElse(), ifPresent() или orElseThrow() с понятным сообщением. И не применяйте Optional для полей и параметров — он задуман как тип результата метода.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.