На этой неделе я узнал об одной интересной "новой" возможности 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); }
-
Оберните
orderId
вOptional
-
Найдите соответствующие строки заказа
-
Используйте
flatMap()
, чтобы получитьOptional<BigDecimal>
;map()
получитOptional<Optional<BigDecimal>>
-
Нам нужно обернуть результат в
Optional
, чтобы он соответствовал сигнатуре метода. -
Если
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". Приглашаем всех желающих на день открытых дверей онлайн, где можно подробнее узнать о формате и программе обучения, а также познакомиться с преподавателем.