Что делает метод flatMap в Optional в Java
Что проверяют
Понимаете ли вы Optional как контейнер, а не как модный способ написать проверку на null. Ключевое в ответе это разница между map и flatMap, потому что именно она показывает, видите ли вы тип результата функции, а не только её смысл.
Как отвечать
flatMap принимает функцию, которая сама возвращает Optional, и не оборачивает результат второй раз. Если исходный контейнер пуст, функция не вызывается и возвращается пустой Optional. У map функция возвращает обычное значение, а результат кладётся в новый Optional, причём через логику ofNullable, поэтому вернувшийся null превращается в пустой контейнер, а не в ошибку.
Optional<String> city = findUser(id) // Optional<User>
.flatMap(User::getAddress) // геттер отдаёт Optional<Address>
.map(Address::getCity); // геттер отдаёт String
String result = city.orElse("не указан");Правило для выбора простое: функция отдаёт Optional, значит flatMap; отдаёт обычное значение, значит map. Дальше цепочку закрывают через orElse, orElseGet или orElseThrow, а не через get.
Где спотыкаются
Используют flatMap там, где достаточно map: flatMap(v -> Optional.of(v.length())) это тот же map с лишней обёрткой. Обратная ошибка встречается чаще и опаснее: map на метод репозитория, который сам возвращает контейнер, и в типе появляется Optional<Optional<User>>, который потом разворачивают вручную.
Возвращают из функции null вместо Optional.empty(). В map это просто даст пустой результат, в flatMap получите NullPointerException, потому что метод ожидает контейнер и сразу его разыменовывает.
Смешивают с flatMap у Stream. Там функция возвращает поток, и все потоки склеиваются в один, а не разворачивается одна обёртка. Мостик между двумя мирами это Optional.stream(), про него полезно вспомнить, когда из списка контейнеров надо собрать список значений.