49

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?

Puce
  • 37,247
  • 13
  • 80
  • 152
Sebastian Oberhoff
  • 1,271
  • 1
  • 10
  • 16
  • 7
    possible duplicate of [Get value from one Optional or another](http://stackoverflow.com/questions/24599996/get-value-from-one-optional-or-another) – Tunaki Sep 25 '15 at 07:21
  • ... when confronted with the problem of null ... now we have two problems. – ZhongYu Sep 25 '15 at 08:01
  • 5
    Btw, Paul Sandoz just published a [changeset](http://cr.openjdk.java.net/~psandoz/jdk9/JDK-8080418-optional-or/webrev/src/java.base/share/classes/java/util/Optional.java.udiff.html) for review which introduces `Optional.or()` method, which does exactly what you want: `firstChoice().or(this::secondChoice)`. More info at [JDK-8080418](https://bugs.openjdk.java.net/browse/JDK-8080418). In Java-9 times we'll be happy! – Tagir Valeev Sep 25 '15 at 12:08

9 Answers9

53

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.

marstran
  • 26,413
  • 5
  • 61
  • 67
  • 44
    Note: Java 9 introduces the `Optional::or` method. This could therefore be written `firstChoice().or(this::secondChoice)` in Java 9. – marstran Jun 02 '16 at 07:44
  • This only works with Java8 if `this::secondChoice` returns a `Supplier` rather than `Optional` - the example from the OP does not. – exception Nov 03 '17 at 22:17
  • 2
    @exception `this::secondChoice` _is_ a `Supplier`. It is a method reference. – marstran Nov 04 '17 at 20:34
  • In the OP's example, that method returns Optional, which is not a Supplier - and I was pointing out that for Java8 it is different. – exception Nov 06 '17 at 12:25
  • 2
    @exception `secondChoice()` returns an `Optional`. `this::secondChoice` is a method reference of type `Supplier>`. If you call the method reference, you get the `Optional`. The example in the answer works for Java 8 and onward. – marstran Nov 06 '17 at 13:47
  • @martstran Argh, overlooked that detail. My bad. – exception Nov 07 '17 at 15:17
6

Maybe something like this:

Optional<String> finalChoice = Optional.ofNullable(firstChoice()
    .orElseGet(() -> secondChoice()
    .orElseGet(() -> null)));

From: Chaining Optionals in Java 8

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Marthym
  • 3,119
  • 2
  • 14
  • 22
5

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.
Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
5

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.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
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]
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
3

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.

Erik
  • 6,470
  • 5
  • 36
  • 37
2

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
);
Pepe-Soft
  • 21
  • 1
0

with java 11 you can simply do firstChoice().or(this::secondChoice)

thahgr
  • 718
  • 1
  • 10
  • 27
-1

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));
Denis Bazhenov
  • 9,680
  • 8
  • 43
  • 65
  • I find this a lot less understandable than the `if` solution in the question, btw. – user253751 Sep 25 '15 at 07:59
  • Well, I agree if the only thing you have is 2 `Optional`s. This is why I stated that this way works for _arbitrary number_ of `Optional`s. – Denis Bazhenov Sep 25 '15 at 08:03
  • The problem of your solution is that it's not short-circuiting: you have to evaluate all the choices which is exactly what the OP tries to avoid. In OPs code `firstChoice()` and `secondChoice()` are actually methods and you cannot simply modify your solution to omit calling the second one if the first produced non-empty optional. – Tagir Valeev Sep 25 '15 at 08:35
  • 1
    What do you mean? `findFirst()` defined as a short-circuting operation in documentation. You can easily check that `Optional.get()` and `Optional.isPresent()` methods called only once using debugger. – Denis Bazhenov Sep 28 '15 at 00:34