You've just stumbled upon a concept called lifting in functional programming, that enables you to lift regular functions (e.g. A -> B) into new domains (e.g. Optional<A> -> Optional<B>).
There's also a syntactic sugar for flatMapping and mapping more comfortably called the do notation in Haskell and similar languages, and for comprehension in Scala. It gives you a way to keep the flow linear and avoid nesting (that you were forced to go through in your example 1).
Java, unfortunately has nothing of the sort, as its functional programming capabilities are meager, and even Optional isn't really a first-class citizen (no standard API actually uses it).
So you're stuck with the approaches you've already discovered.
In case you're curious about the concepts mentioned above, read on.
Lifting
Assuming you have:
public String f(A a, B b) {
return b + "-" + a;
}
With its Scala equivalent:
def f(a: A, b: B) = b + "-" + a
Lifting f into Option (same as Optional in Java) would look like this (using Scalaz library, see here for Cats)
val lifted = Monad[Option].lift2(f)
lifted is now a function equivalent to:
public Optional<String> f(Optional<A> a, Optional<B> b) {
if(a.isPresent() && b.isPresent()) {
return Optional.of(b + "-" + a);
}
return Optional.empty;
}
Exactly what you're looking for, in 1 line, and works for any context (e.g. List, not just Option) and any function.
For comprehension / Do notation
Using for comprehension, your example would look like this (I think, my Scala is weak):
for {
a <- getA();
b <- getB();
} yield f(a, b)
And again, this is applicable to anything that can be flatMapped over, like List, Future etc.
get()in the first means that you'll get an Exception if eitherOptionalis empty, so definitely not the first method.orElse..candidate for (1). This is an unclear problem and is driving opinions... and another question that puzzles me at least is, what does the methodfdo such that you need to ensure that both(/all) the values are present before calling it? Just wondering could f accept a varargs such asObject f(String... args)?