Pull to refresh
834.72
OTUS
Цифровые навыки от ведущих экспертов

Optional.stream()

Reading time 2 min
Views 8.3K
Original author: Nicolas Fränkel

На этой неделе я узнал об одной интересной "новой" возможности Optional, о которой хочу рассказать в этом посте. Она доступна с Java 9, так что новизна ее относительна.

Давайте начнем со следующей последовательности для вычисления общей цены заказа:

public BigDecimal getOrderPrice(Long orderId) {
    List<OrderLine> lines = orderRepository.findByOrderId(orderId);
    BigDecimal price = BigDecimal.ZERO;       
    for (OrderLine line : lines) {
        price = price.add(line.getPrice());   
    }
    return price;
}
  • Предоставьте переменную-аккумулятор для цены

  • Добавьте цену каждой строки к общей цене

В настоящее время, вероятно, более целесообразно использовать стримы вместо итераций. Следующий фрагмент эквивалентен предыдущему:

public BigDecimal getOrderPrice(Long orderId) {
    List<OrderLine> lines = orderRepository.findByOrderId(orderId);
    return lines.stream()
                .map(OrderLine::getPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
}

Давайте сосредоточимся на переменной orderId : она может содержать null.

Императивный способ обработки null заключается в том, чтобы проверить его в начале метода - и в конечном итоге сбросить:

public BigDecimal getOrderPrice(Long orderId) {
    if (orderId == null) {
        throw new IllegalArgumentException("Order ID cannot be null");
    }
    List<OrderLine> lines = orderRepository.findByOrderId(orderId);
    return lines.stream()
                .map(OrderLine::getPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
}

Функциональный способ заключается в том, чтобы обернуть orderId в Optional. Вот как выглядит код с использованием Optional:

public BigDecimal getOrderPrice(Long orderId) {
    return Optional.ofNullable(orderId)                            
            .map(orderRepository::findByOrderId)                   
            .flatMap(lines -> {                                    
                BigDecimal sum = lines.stream()
                        .map(OrderLine::getPrice)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                return Optional.of(sum);                           
            }).orElse(BigDecimal.ZERO);                            
}
  1. Оберните orderId в Optional

  2. Найдите соответствующие строки заказа

  3. Используйте flatMap(), чтобы получить Optional<BigDecimal>; map() получит Optional<Optional<BigDecimal>>

  4. Нам нужно обернуть результат в Optional, чтобы он соответствовал сигнатуре метода.

  5. Если Optional не содержит значения, сумма равна 0

Optional делает код менее читабельным! Я считаю, что понятность должна быть всегда важнее стиля кода.

К счастью, Optional предлагает метод stream() (начиная с Java 9). Он позволяет упростить функциональный конвейер:

public BigDecimal getOrderPrice(Long orderId) {
    return Optional.ofNullable(orderId)
            .stream()
            .map(orderRepository::findByOrderId)
            .flatMap(Collection::stream)
            .map(OrderLine::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
}

Вот краткая информация о типе на каждой строке:

Функциональный код не обязательно означает то, что это также читаемый код. Учитывая последние изменения, я считаю, что это и то, и другое.


Перевод материала подготовлен в рамках курса "Java Developer. Basic". Приглашаем всех желающих на день открытых дверей онлайн, где можно подробнее узнать о формате и программе обучения, а также познакомиться с преподавателем.

Tags:
Hubs:
-6
Comments 4
Comments Comments 4

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS