Looking for a way to chain optionals so that the first one that is present is returned. If none are present Optional.empty()
should be returned.
Assuming I have several methods like this:
Optional<String> find1()
I'm trying to chain them:
Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );
but of course that doesn't work because orElse
expects a value and orElseGet
expects a Supplier
.
The need for Optional In any Java application, it is a common pattern to use method chaining to de-reference object properties. This often leads to the NullPointerException whenever there is a null object reference.
Advertisements. Optional is a container object used to contain not-null objects. Optional object is used to represent null with absent value. This class has various utility methods to facilitate code to handle values as 'available' or 'not available' instead of checking null values.
In a nutshell, the Optional class includes methods to explicitly deal with the cases where a value is present or absent. However, the advantage compared to null references is that the Optional class forces you to think about the case when the value is not present.
Use a Stream:
Stream.of(find1(), find2(), find3())
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
If you need to evaluate the find methods lazily, use supplier functions:
Stream.of(this::find1, this::find2, this::find3)
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
Inspired by Sauli's answer, it is possible to use the flatMap()
method.
Stream.of(this::find1, this::find2, this::find3)
.map(Supplier::get)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
Converting an Optional into a Stream is cumbersome. Apparently, this is going to be fixed with JDK9. So this could be written as
Stream.of(this::find1, this::find2, this::find3)
.map(Supplier::get)
.flatMap(Optional::stream)
.findFirst();
Update after Java 9 was released
Although the original question was about Java 8, Optional::or
was introduced in Java 9. With it, the problem could be solved as follows
Optional<String> result = find1()
.or(this::find2)
.or(this::find3);
You could do it like this:
Optional<String> resultOpt = Optional.of(find1()
.orElseGet(() -> find2()
.orElseGet(() -> find3()
.orElseThrow(() -> new WhatEverException()))));
Though I'm not sure it improves readability IMO. Guava provides a way to chain Optionals:
import com.google.common.base.Optional;
Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());
It could be another alternative for your problem but does not use the standard Optional class in the JDK.
If you want to keep the standard API, you could write a simple utility method:
static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
return first.isPresent() ? first : second;
}
and then:
Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));
If you have a lot of optionals to chains, maybe it's better to use the Stream approach as other mentionned already.
most likely case the readers are looking for (today)
result = find1()
.or(this::find2)
.or(this::find3);
result = Optional.ofNullable(find1()
.orElse(find2()
.orElse(find3()
.orElse(null))));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With