Optional в Java: map, flatMap и обработка отсутствующего значения

Optional в Java: map, flatMap и обработка отсутствующего значения
Содержание статьи

Optional в Java явно сообщает, что результат может отсутствовать. Вместо вызова get с надеждой на наличие данных лучше описать, что сделать со значением и что вернуть или выбросить при его отсутствии. Это особенно полезно на границе поиска объекта и бизнес-логики.

В разборе Mentoring.digital используем методы, доступные в Java 8 и новее. Optional не устраняет все null в программе и не заменяет обработку ошибок выполнения.

Сначала отделите отсутствие от пустой строки

import java.util.Optional;

public class OptionalDemo {
    static Optional<String> cleanName(String raw) {
        return Optional.ofNullable(raw)
            .map(String::trim)
            .filter(value -> !value.isEmpty());
    }

    static String fallback() {
        System.out.println("Запасной вариант вычислен");
        return "Гость";
    }

    public static void main(String[] args) {
        String displayName = cleanName("  Анна  ")
            .orElseGet(OptionalDemo::fallback);
        System.out.println(displayName);

        String guest = cleanName("   ").orElse("Гость");
        System.out.println(guest);
    }
}

ofNullable превращает null в пустой Optional. map обрезает пробелы, если строка есть, а filter отбрасывает пустой результат. Для null, пустой строки и строки из пробелов теперь действует одно явно выбранное правило. Если по требованиям эти случаи различаются, объединять их нельзя.

В первом вызове fallback не запускается, потому что имя найдено. Программа печатает Анна, затем Гость. Проверьте отдельно null, пробелы и обычное имя. trim в этом упражнении не является универсальной нормализацией всех пробельных символов Unicode.

Чем orElse отличается от orElseGet

Аргумент метода вычисляется до вызова метода. Поэтому выражение optional.orElse(fallback()) запустит fallback даже при наличии значения. orElseGet принимает поставщика и вызывает его только для пустого Optional. Для готовой константы orElse вполне подходит; для вычисления или операции с побочным эффектом различие существенно.

Не используйте этот выбор как повод скрыть ошибку обращения к базе запасным результатом. Отсутствующая запись и недоступная база означают разные ситуации. Optional подходит для первого случая, а второй должен сохранить свою ошибку.

Когда нужен flatMap

Если функция уже возвращает Optional, обычный map создаст вложенный контейнер Optional<Optional<T>>. flatMap позволяет продолжить цепочку без этого уровня. Сам переданный в flatMap метод не должен возвращать null.

static Optional<String> departmentEmail(String department) {
    return "support".equals(department)
        ? Optional.of("help@example.test")
        : Optional.empty();
}

// Внутри метода main:
Optional<String> email = cleanName(" support ")
    .flatMap(OptionalDemo::departmentEmail);
String requiredEmail = email.orElseThrow(
    () -> new IllegalArgumentException("Отдел без адреса")
);

Добавьте departmentEmail как статический метод OptionalDemo, а оставшуюся часть внутрь main. Адрес example.test здесь учебный, отправки сообщений нет. При неизвестном отделе orElseThrow выдаст выбранное исключение. При известном отделе вернётся строка.

Что проверить на ревью

  • Метод, объявленный как возвращающий Optional, никогда не возвращает null.
  • get не вызывается без гарантии наличия значения.
  • Запасное вычисление не запускается преждевременно через orElse.
  • Цепочка не теряет различие между отсутствием и ошибкой.
  • Optional не используется как автоматическая замена каждому полю класса или параметру.

Цепочки преобразований удобно отрабатывать на курсе Java с наставником. Если сомневаетесь в контракте метода, покажите вызывающий код ментору по Java, а не только отдельный Optional.

Похожие статьи по данной теме