Como utilizar Optional.stream()

Olá! Tudo bem com vocês? Depois de algum tempo ausente, finalmente estou disponível para continuar escrevendo em meu blog.

Hoje vou tentar demonstrar como deixar seu código mais limpo, ajudando assim na leitura do mesmo e também a vida das pessoas que fazem review do seu código java.

Optional.stream() é uma feature do Java 9, porém mesmo inserido na JDK há tanto tempo, muitos ainda não utilizam-o, ou não em sua complexidade.

Vamos iniciar com a sequência de como seria para computar o valor total de um pedido:

public BigDecimal getOrderPrice(Long orderId) {
    List<OrderItem> items = orderRepository.findByOrderId(orderId);
    BigDecimal price = BigDecimal.ZERO;       
    for (OrderItem orderItem : items) {
        price = price.add(orderItem.getPrice());   
    }
    return price;
}
  1. Primeiro vamos ter uma variável para acumular o preço.
  2. A cada iteração adicionamos valor à nossa variável.

Hoje em dia, é provavelmente mais adequado usar streams em vez de iterações. Esse snippet é equivalente ao anterior:

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

Temos um problema, pois a variável orderId pode ser nula.

Bem, a maneira imperativa de lidar com valores nulos é verificá-los no início do método e, se for o caso, lançar uma exceção.

Vamos atualizar nosso código:

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

Agora vamos tentar repeti-lo em uma maneira funcional. Vamos envolver o orderId em um optional.

Esta é a aparência do código utilizando Optional:

public BigDecimal getOrderPrice(Long orderId) {
    return Optional.ofNullable(orderId)                            
            .map(orderRepository::findByOrderId)                   
            .flatMap(items -> {                                    
                BigDecimal sum = items.stream()
                        .map(OrderItem::getPrice)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                return Optional.of(sum);                           
            }).orElse(BigDecimal.ZERO);                            
}
  • Envolva o orderId em um Optional;
  • Encontre os items do pedido;
  • Use flatMap() para obter um Optional<BigDecimal>, nesse caso pode surgir o questionamento, de porque não utilizar um map(), porém o map() obteria um Optional<Optional<BigDecimal>>, e não é isso que queremos.
  • Precisamos colocar o resultado em um Optional para estar em conformidade com a assinatura do método;
  • Se o Optional não contém um valor, a soma é 0.

Dessa forma acredito que o Opcional torna o código até menos legível, que antes…

Mas sempre acreditei no princípio de que a legibilidade deve superar o estilo do código.

E aí que felizmente, Optional oferece um método stream() (desde Java 9) que permite simplificar o pipeline funcional dessa forma:

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

Agora vou explicar linha a linha do pipeline:

Optional.ofNullable(orderId)Optional<Long>
stream()Stream<Long>
map(orderRepository::findByOrderId)Stream<List<OrderLine>>
flatMap(Collection::stream)Stream<OrderLine>
map(OrderItem::getPrice)Stream<BigDecimal>
reduce(BigDecimal.ZERO, BigDecimal::add)BigDecimal

Código funcional não significa necessariamente código legível. Porém com essas mudanças, acredito que sejam as duas coisas.

Espero que passem a utilizar Optional.stream(), ou que melhorem a dinâmica do seu uso.

Obrigado.

🙂

Published by Ivan Marrêta

Software Engineer

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: