Is there a Java method or a succinct approach to combine two Optional
instances using a mapping function if both are present; otherwise produce one or the other that has a value?
Here is how it would look conceptually in a clunky, non-functional approach:
<T> Optional<T> mapOr(Optional<T> optional1, Optional<T> optional2, BinaryOperator<T> fun)
if(optional1.isPresent() && optional2.isPresent()) {
return Optional.of(fun.apply(optional.get(), optional2.get()));
} else {
return optional1.or(() -> optional2);
}
}
(I would never want to write code like that; I'm just spelling out the semantics.)
As a hypothetical use case, let's say I have two optional things I want to link together (like in a linked list), but I don't know if either exists. I want to end up with the first thing, optionally linking to the second one, through some hypothetical this.link(…)
function.
Optional<E> linkedList = mapOr(optionalElement1, optionalElement2, this::link);
A more functional implementation approach might be:
<T> Optional<T> mapOr(Optional<T> optional1, Optional<T> optional2, BinaryOperator<T> fun) {
return optional1.map(o1 -> optional2.map(o2 -> fun.apply(o1, o2))
.or(() -> optional1)).or(() -> optional2);
}
But that is hideous.
Is there some common functional paradigm I'm missing here? It almost reminds me of a stream reduction operation, that is something like this:
<T> Optional<T> mapOr(Optional<T> optional1, Optional<T> optional2, BinaryOperator<T> fun) {
return Stream.of(optional1, optional2)
.filter(Optional::isPresent)
.map(Optional::get)
.reduce(fun);
}
What's cool about that approach is that it can be generalized to any number of Optional
s. (I'm skeptical about its performance, though.) Still I would have to create a utility method—I wouldn't want to use something that wordy over and over (and I need to use this in several places). Later I realized I could convert the Optional
s to streams, concat them, and then perform the reduce (as Holger mentions in one of the answers); that would be slightly prettier functional programming, but still pretty verbose.
Unfortunately after more thought, I realize that this is not exactly what I need; rather than link(link(link(1, 2), 3), 4)
, I need link(1, link(2, link(3, 4)))
. Further research reveals that what I want is a "right fold". I don't immediately see a right fold stream operation in Java. Nevertheless this would work with only two values.
Does Java have something more concise made for this operation already?
All of your approaches can be slightly simplified.
The non-functional approach
<T> Optional<T> mapOr(Optional<T> value1, Optional<T> value2, BinaryOperator<T> fun) {
return value1.isPresent()? value2.isPresent()?
Optional.of(fun.apply(value1.get(), value2.get())): value1: value2;
}
The first functional approach
<T> Optional<T> mapOr(Optional<T> value1, Optional<T> value2, BinaryOperator<T> fun) {
return value1.map(t1 -> value2.map(t2 -> fun.apply(t1, t2)).orElse(t1))
.or(() -> value2);
}
The Stream approach
<T> Optional<T> mapOr(Optional<T> value1, Optional<T> value2, BinaryOperator<T> fun) {
return Stream.concat(value1.stream(), value2.stream()).reduce(fun);
}
Considering that Java has no built-in operators we could utilize here, these are already the most concise ways to express this operation.