Reputation: 1311
Here's what I've got so far:
Optional<Foo> firstChoice = firstChoice();
Optional<Foo> secondChoice = secondChoice();
return Optional.ofNullable(firstChoice.orElse(secondChoice.orElse(null)));
This strikes me as both hideous and wasteful. If firstChoice is present I am needlessly computing secondChoice.
There's also a more efficient version:
Optional<Foo> firstChoice = firstChoice();
if(firstChoice.isPresent()) {
return firstChoice;
} else {
return secondChoice();
}
Here I can't chain some mapping function to the end without either duplicating the mapper or declaring another local variable. All of this makes the code more complicated than the actual problem being solved.
I'd much rather be writing this:
return firstChoice().alternatively(secondChoice());
However Optional::alternatively obviously doesn't exist. Now what?
Upvotes: 51
Views: 39079
Reputation: 28036
Edit:
From Java 9 and onwards, you can use the .or
method on Optional
.
Like this:
firstChoice().or(this::secondChoice);
Try this:
firstChoice()
.map(Optional::of)
.orElseGet(this::secondChoice);
The map method gives you an Optional<Optional<Foo>>
. Then, the orElseGet
method flattens this back to an Optional<Foo>
. The secondChoice
method will only be evaluated if firstChoice()
returns the empty optional.
Upvotes: 55
Reputation: 21122
Java 9 added the Optional.or(supplier)
method for this sort of situation.
return firstChoice().or(this::secondChoice);
Given methods firstChoice()
and secondChoice()
which each return Optional<Foo>
, the above one-liner uses Optional.or
to achieve the desired result.
As desired, this approach only computes secondChoice
when firstChoice
is empty.
Upvotes: 7
Reputation: 3219
Maybe something like this:
Optional<String> finalChoice = Optional.ofNullable(firstChoice()
.orElseGet(() -> secondChoice()
.orElseGet(() -> null)));
From: Chaining Optionals in Java 8
Upvotes: 6
Reputation: 21
Lazy computations and arbitrary number of Optional
elements
Stream.<Supplier<Optional<Foo>>>of(
this::firstChoice,
this::secondChoice
).map(
Supplier::get
).filter(
Optional::isPresent
).findFirst(
).orElseGet(
Optional::empty
);
Upvotes: 2
Reputation: 7342
I was frustrated enough by the fact that this wasn't supported in java 8, that I switched back to guava's optionals which have or
:
public abstract Optional<T> or(Optional<? extends T> secondChoice)
Returns this Optional if it has a value present; secondChoice otherwise.
Upvotes: 3
Reputation: 100209
Here's the generalization of @marstran solution for any number of optionals:
@SafeVarargs
public static <T> Optional<T> selectOptional(Supplier<Optional<T>>... optionals) {
return Arrays.stream(optionals)
.reduce((s1, s2) -> () -> s1.get().map(Optional::of).orElseGet(s2))
.orElse(Optional::empty).get();
}
Test:
public static Optional<String> first() {
System.out.println("foo called");
return Optional.empty();
}
public static Optional<String> second() {
System.out.println("bar called");
return Optional.of("bar");
}
public static Optional<String> third() {
System.out.println("baz called");
return Optional.of("baz");
}
public static void main(String[] args) {
System.out.println(selectOptional(() -> first(), () -> second(), () -> third()));
}
Output:
foo called
bar called
Optional[bar]
Upvotes: 4
Reputation: 9945
Here is a way which works for arbitrary number of Optional
's based in a stream API:
return Arrays.asList(firstChoice, secondChoice).stream()
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst().orElse(null);
It's not the shortest one. But more plain and understandable.
Another way is to use firstNonNull()
from Guava of commons-lang if you are already using one of those libraries:
firstNonNull(firstChoice.orElse(null), secondChoice.orElse(null));
Upvotes: -1
Reputation: 121998
You can simply replace that with,
Optional<Foo> firstChoice = firstChoice();
return firstChoice.isPresent()? firstChoice : secondChoice();
The above code won't call unless firstChoice.isPresent() is false.
But you have to be prepare to call both functions to get the desired output. There is no other way to escape the checking.
Upvotes: 5