问题
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?
回答1:
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.
回答2:
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.
- Best case is First choice returning true.
- Worst case will be First choice returning false, hence another method call for second choice.
回答3:
Maybe something like this :
Optional<String> finalChoice = Optional.ofNullable(firstChoice()
.orElseGet(() -> secondChoice()
.orElseGet(() -> null)));
From : Chaining Optionals in Java 8
回答4:
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]
回答5:
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.
回答6:
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
);
回答7:
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));
来源:https://stackoverflow.com/questions/32776705/whats-the-most-elegant-way-to-combine-optionals