Dario Viva
Dario Viva

Reputation: 267

How to chain Optional ifPresent calls?

Suppose you have an Optional and you want to consume the Optional multiple times. You could now save the Optional to a variable; and then use ifPresent two times on it:

Optional<Animal> optionalAnimal = animalService.getAllAnimals().findFirst();
optionalAnimal.ifPresent(Animal::eat);
optionalAnimal.ifPresent(Animal::drink);

Another solution would be to ditch method references and use a lambda that does both:

animalService.getAllAnimals().findFirst()
    .ifPresent(animal -> {
        animal.drink();
        animal.eat();
    });

If I have control over the class that is used in the Optional, I could simply change the methods to use a factory like pattern. So that animal.drink() would return itself. Then I could write:

animalService.getAllAnimals().findFirst()
    .map(Animal::drink)
    .ifPresent(Animal::eat);

But this would be semantically weird. And I don’t always have control over every class that I use in Optionals. And some classes are final, so I could not even extend them to have a factory styled method.

Furthermore, the Optional class is also final, so extending Optional itself is no option either. All of this makes very little sense to me. ifPresent() returns void. If ifPresent() returned the Optional itself (similar to peek() for streams) it would be closer to my goal.

Is there another solution that I did not think of?


What I would like to have is something like this:

animalService.getAllAnimals().findFirst()
    .ifPresent(Animal::drink)
    .ifPresent(Animal::eat);

Upvotes: 2

Views: 1245

Answers (2)

cyberbrain
cyberbrain

Reputation: 5075

Build a wrapper

Your idea of inheritance had a small flaw as you found out for yourself, but composition should be favoured in most cases anyway, so you could simply build a wrapper for Optional that does what you want:

FluentOptional

package examples.stackoverflow.q74467465;

import java.util.Optional;
import java.util.function.Consumer;

public class FluentOptional<T> {

  private final Optional<T> optional;

  public FluentOptional(Optional<T> optional) {
    this.optional = optional;
  }

  public static <S> FluentOptional<S> of(Optional<S> optional) {
    return new FluentOptional<>(optional);
  }

  public FluentOptional<T> ifPresent(Consumer<? super T> consumer) {
    this.optional.ifPresent(consumer);
    return this;
  }
}

You can use it like this:

FluentOptional.of(animalService.getAllAnimals().findFirst())
    .ifPresent(Animal::drink)
    .ifPresent(Animal::eat);

Upvotes: 0

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28978

Consumer.andThen()

As alternative, you can introduce a method that allows to combine multiple Consumers using Consumer.andThen() and then perform all required side-effects by passing an aggregated Consumer into Optional.ifPresent().

That's how such method might look like (credits to @Lino for the idea with the first Consumer as an identity):

@SafeVarargs
public static <T> Consumer<T> combine(Consumer<T> first, Consumer<T>... others) {
    
    return Arrays.stream(others).reduce(first, Consumer::andThen);
}

Usage example:

animalService.getAllAnimals()
    .ifPresent(combine(Animal::drink, Animal::eat));

Upvotes: 6

Related Questions