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.
Assumi
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.
To perform Optional Chaining First convert Stream to Optional Using either of the two methods
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
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();
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.
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);