Chaining Optionals in Java 8
JavaLambdaJava 8OptionalJava Problem Overview
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
.
Java Solutions
Solution 1 - Java
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();
Solution 2 - Java
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);
Solution 3 - Java
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.
Solution 4 - Java
Based on Alexis C's answer, but without the nesting of orElse
s
String result = find1()
.map(Optional::of)
.orElseGet(Foo::find2())
.map(Optional::of)
.orElseGet(Foo::find3())
.orElseThrow(() -> new WhatEverException())
Remove the orElseThrow
if you want an Optional<String>
as the result instead.
The trick is to wrap each optional returned by the findX
to another optional before each orElseGet
.
Solution 5 - Java
// Java 9+
find1().or(() -> find2()).or(() -> find3());
// Java 8
Optional.ofNullable(
find1().orElse(
find2().orElse(
find3().orElse( null )
)));
Solution 6 - Java
for cascading chaining You could use ifPresentOrElse
find1().ifPresentOrElse( System.out::println, new Runnable() {
public void run() {
find2().ifPresentOrElse( System.out::println, new Runnable() {
public void run() {
find3().ifPresentOrElse( System.out::println, new Runnable() {
public void run() {
System.err.println( "nothing found…" );
}
} );
}
} );
}
} );
to do something with the value of the Optional
You had to replace the System.out::println
with Your Consumer
(different Consumers would also be possible in this solution)
Solution 7 - Java
My common generic solution for all those issues:
public static <T> T firstMatch(final Predicate<T> matcher, final T orElse, final T... values) {
for (T t : values) {
if (matcher.test(t)) {
return t;
}
}
return orElse;
}
Then you can do:
public static <T> Optional<T> firstPresent(final Optional<T>... values) {
return firstMatch(Optional::isPresent, Optional.empty(), values);
}
Solution 8 - Java
To perform Optional Chaining First convert Stream to Optional Using either of the two methods
- findAny() or findFirst()
- min() / max()
Once optional is obtained optional has two more instance method which are also present in Stream class i.e filter and map(). use these on methods and to check output use ifPresent(System.out :: Println)
ex:
Stream s = Stream.of(1,2,3,4);
s.findFirst().filter((a)->a+1).ifPresent(System.out :: Println)
Output is : 2
Solution 9 - Java
Maybe one of
public <T> Optional<? extends T> firstOf(Optional<? extends T> first, @SuppressWarnings("unchecked") Supplier<Optional<? extends T>>... supp) {
if (first.isPresent()) return first;
for (Supplier<Optional <? extends T>> sup : supp) {
Optional<? extends T> opt = sup.get();
if (opt.isPresent()) {
return opt;
}
}
return Optional.empty();
}
public <T> Optional<? extends T> firstOf(Optional<? extends T> first, Stream<Supplier<Optional<? extends T>>> supp) {
if (first.isPresent()) return first;
Stream<Optional<? extends T>> present = supp.map(Supplier::get).filter(Optional::isPresent);
return present.findFirst().orElseGet(Optional::empty);
}
will do.
The first one iterates over an array of suppliers. The first non-empty Optional<>
is returned. If we don't find one, we return an empty Optional
.
The second one does the same with a Stream
of Suppliers
which is traversed, each one asked (lazily) for their value, which is then filtered for empty Optional
s. The first non-empty one is returned, or if no such exists, an empty one.