← Назад к вопросам
Какие знаешь способы передачи лямбда функции в Optional?
1.3 Junior🔥 141 комментариев
#Stream API и функциональное программирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы передачи лямбда функций в Optional
Optional (Java 8+) - мощный инструмент для работы с null-значениями. Поддерживает функциональное программирование через лямбда-функции.
1. map() - трансформация значения
Применяет функцию к значению если оно присутствует:
Optional<String> name = Optional.of("John");
// Лямбда функция: String -> String
Optional<Integer> length = name.map(s -> s.length());
// Результат: Optional[4]
// Цепочка map
Optional<String> result = name
.map(String::toUpperCase) // JOHN
.map(s -> s.substring(0, 2)) // JO
.map(s -> "Hello " + s); // Hello JO
System.out.println(result.orElse("N/A")); // Hello JO
Важно: Если Optional пустой, map пропускает функцию:
Optional<String> empty = Optional.empty();
Optional<Integer> result = empty.map(String::length);
// Результат: Optional.empty (функция не выполнилась)
2. flatMap() - для функций возвращающих Optional
Используй когда функция возвращает Optional:
// Без flatMap - вложенные Optional
Optional<User> user = getUserById(1);
Optional<Optional<Address>> address = user.map(u -> u.getAddress());
// Тип: Optional<Optional<Address>> - неудобно!
// С flatMap - развёртывание
Optional<Address> address = getUserById(1)
.flatMap(user -> user.getAddress()); // Optional<Address>
// Цепочка flatMap
Optional<String> city = getUserById(1)
.flatMap(user -> user.getAddress()) // Optional<Address>
.flatMap(addr -> addr.getCity()) // Optional<String>
.map(String::toUpperCase); // Optional<String>
System.out.println(city.orElse("Unknown")); // MOSCOW
Практический пример:
// Поиск пользователя и его города
public Optional<String> getUserCity(Long userId) {
return userRepository.findById(userId)
.flatMap(user -> Optional.ofNullable(user.getAddress()))
.flatMap(address -> Optional.ofNullable(address.getCity()))
.map(String::trim); // Удаляем пробелы
}
// Использование
userRepository.findById(1)
.flatMap(user -> user.getOrders().stream().findFirst())
.map(order -> order.getTotal())
.ifPresentOrElse(
total -> System.out.println("Total: " + total),
() -> System.out.println("No orders")
);
3. filter() - условная фильтрация
Применяет лямбда-предикат:
Optional<User> user = Optional.of(new User("John", 25));
// Фильтр: если возраст >= 18
Optional<User> adult = user.filter(u -> u.getAge() >= 18);
// Результат: Optional[User]
Optional<User> underage = user.filter(u -> u.getAge() < 18);
// Результат: Optional.empty
// Цепочка фильтров
Optional<User> validUser = user
.filter(u -> u.getAge() >= 18) // Проверка возраста
.filter(u -> u.getEmail() != null) // Проверка email
.filter(u -> u.isActive()); // Проверка статуса
if (validUser.isPresent()) {
System.out.println("Valid user found");
}
4. ifPresent() и ifPresentOrElse()
Выполнение функции на основе наличия значения:
Optional<User> user = getUserById(1);
// ifPresent - выполнить если есть
user.ifPresent(u -> System.out.println("User: " + u.getName()));
// Эквивалент без Optional
if (user.isPresent()) {
System.out.println("User: " + user.get().getName());
}
// ifPresentOrElse - две ветки (Java 9+)
user.ifPresentOrElse(
u -> System.out.println("Found: " + u.getName()),
() -> System.out.println("User not found")
);
// Сложный пример
getOrderById(123).ifPresentOrElse(
order -> {
order.setStatus("PROCESSING");
saveOrder(order);
sendNotification(order.getCustomerId(), "Order received");
},
() -> {
logger.warn("Order 123 not found");
throw new OrderNotFoundException();
}
);
5. or() - альтернативное значение (Java 9+)
Возвращает другой Optional если первый пуст:
Optional<User> primaryUser = getUserById(1);
Optional<User> backupUser = getUserById(2);
// Если primary пуст, используй backup
Optional<User> result = primaryUser.or(() -> backupUser);
// Цепочка or
Optional<User> user = getUserById(1)
.or(() -> getUserById(2))
.or(() -> getUserById(3))
.or(() -> Optional.of(getDefaultUser()));
6. Комбинирование нескольких Optional
public record Transaction(
String userId,
String accountId,
BigDecimal amount
) {}
// Классический способ
Optional<User> user = findUser(id);
Optional<Account> account = findAccount(id);
Optional<BigDecimal> limit = findLimit(id);
if (user.isPresent() && account.isPresent() && limit.isPresent()) {
Transaction tx = new Transaction(
user.get().getId(),
account.get().getId(),
limit.get()
);
}
// С функциональным стилем (Java 8 способ)
Optional<Transaction> tx = user.flatMap(u ->
account.flatMap(a ->
limit.map(l -> new Transaction(u.getId(), a.getId(), l))
)
);
tx.ifPresent(this::processTransaction);
// С Java 9+ (combinators библиотека)
Optional<Transaction> result =
Optional.of(Transaction::new)
.flatMap(constructor -> user.map(constructor::apply))
.flatMap(f -> account.map(a -> f.apply(a.getId())))
.flatMap(f -> limit.map(f::apply));
7. peek() - побочные эффекты для отладки
Выполняет функцию без изменения значения:
User user = getUserById(1)
.peek(u -> System.out.println("Found user: " + u.getName()))
.peek(u -> logger.debug("Age: " + u.getAge()))
.peek(u -> metrics.recordUserAccess(u.getId()))
.filter(u -> u.getAge() >= 18)
.peek(u -> System.out.println("Verified adult"))
.orElse(null);
// Идеально для debugging
Optional<String> result = Optional.of("hello")
.peek(s -> System.out.println("Step 1: " + s))
.map(String::toUpperCase)
.peek(s -> System.out.println("Step 2: " + s))
.map(s -> s.substring(0, 2))
.peek(s -> System.out.println("Step 3: " + s));
8. Условные лямбды в комплексных сценариях
// Валидация и обработка
public boolean isValidOrder(String orderId) {
return findOrder(orderId)
.filter(order -> order.getStatus() == OrderStatus.PENDING)
.filter(order -> order.getTotalAmount().compareTo(BigDecimal.ZERO) > 0)
.filter(order -> isAddressValid(order.getDeliveryAddress()))
.isPresent();
}
// Трансформация с валидацией
public Optional<OrderDTO> getValidatedOrder(String orderId) {
return findOrder(orderId)
.filter(order -> order.getStatus() == OrderStatus.CONFIRMED)
.map(order -> {
OrderDTO dto = new OrderDTO();
dto.setId(order.getId());
dto.setTotal(order.calculateTotal());
dto.setEstimatedDelivery(order.getEstimatedDelivery());
return dto;
})
.filter(dto -> dto.getTotal().compareTo(new BigDecimal("100")) >= 0);
}
// Обработка с логированием
public String processUser(Long userId) {
return findUser(userId)
.peek(user -> logger.info("Found user: {}", user.getName()))
.filter(user -> user.isActive())
.peek(user -> metrics.recordActiveUser(user.getId()))
.map(User::getEmail)
.peek(email -> logger.debug("Processing email: {}", email))
.map(this::sendEmail)
.map(result -> "Email sent successfully")
.orElse("User not found or inactive");
}
9. Stream + Optional комбинация
// Получить первый активный пользователь
Optional<User> firstActive = users.stream()
.filter(User::isActive)
.findFirst();
// С дополнительной обработкой
List<String> emails = users.stream()
.map(User::getEmail)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// Более правильный способ (Java 16+)
List<String> emails = users.stream()
.map(User::getEmail)
.flatMap(Optional::stream) // Optional.stream() (Java 9+)
.collect(Collectors.toList());
10. Pattern Matching (Java 16+)
// Будущее Java
Object obj = Optional.of("hello");
if (obj instanceof Optional<String> opt && opt.isPresent()) {
String value = opt.get();
System.out.println("String: " + value);
}
Шпаргалка: Какую операцию использовать
Операция Сигнатура Когда использовать
───────────────────────────────────────────────────────────────
map() T -> R Трансформация значения
flatMap() T -> Optional<R> Вложенные Optional
filter() T -> boolean Условная фильтрация
ifPresent() T -> void Выполнение действия
peek() T -> void Отладка/логирование
or() () -> Optional<T> Альтернатива
orElse() T defaultValue Дефолтное значение
orElseGet() () -> T Lazy дефолтное значение
orElseThrow() () -> Exception Исключение при пусто
Рекомендации
- Используй flatMap() для вложенных Optional - избегай Optional<Optional<T>>
- Цепуй операции - вместо множественных if
- filter() перед map() - более читаемо
- ifPresentOrElse() вместо if-else - функциональный стиль
- Избегай .get() без проверки - опасно!
- peek() только для отладки - не для логики
- Stream<Optional> -> flatMap(Optional::stream) - Java 9+ best practice