Issue
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. But in my Opinion this is really inelegant.
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 lamda that does both. But this looks worse I think.
animalService.getAllAnimals().findFirst()
.ifPresent(animal -> {
animal.drink();
animal.eat();
});
If I have control over the Class that is used in the Optional, I could simly 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 firstly this would be semantically weird. And secondly 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. And apropo; 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. Why did the API designers make this method return void? I think an ifPresent() returning the Optional itself (similar to peek() for streams) would make way more sense. And is there a solution that I did not though of?
What I would like to have is something like this:
animalService.getAllAnimals().findFirst()
.ifPresent(Animal::drink)
.ifPresent(Animal::eat);
Solution
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));
Answered By - Alexander Ivanchenko
Answer Checked By - Clifford M. (JavaFixing Volunteer)