Chaining Optionals in Java 8

JavaLambdaJava 8Optional

Java Problem Overview


Looking for a way to chain optionals so that the first one that is present is returned. If none are present Optional.empty() should be returned.

Assuming I have several methods like this:

Optional<String> find1()

I'm trying to chain them:

Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );

but of course that doesn't work because orElse expects a value and orElseGet expects a Supplier.

Java Solutions


Solution 1 - Java

Use a Stream:

Stream.of(find1(), find2(), find3())
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

If you need to evaluate the find methods lazily, use supplier functions:

Stream.of(this::find1, this::find2, this::find3)
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

Solution 2 - Java

Inspired by Sauli's answer, it is possible to use the flatMap() method.

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
  .findFirst();

Converting an Optional into a Stream is cumbersome. Apparently, this is going to be fixed with JDK9. So this could be written as

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(Optional::stream)
  .findFirst();

Update after Java 9 was released

Although the original question was about Java 8, Optional::or was introduced in Java 9. With it, the problem could be solved as follows

Optional<String> result = find1()
  .or(this::find2)
  .or(this::find3);

Solution 3 - Java

You could do it like this:

Optional<String> resultOpt = Optional.of(find1()
                                .orElseGet(() -> find2()
                                .orElseGet(() -> find3()
                                .orElseThrow(() -> new WhatEverException()))));

Though I'm not sure it improves readability IMO. Guava provides a way to chain Optionals:

import com.google.common.base.Optional;

Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());

It could be another alternative for your problem but does not use the standard Optional class in the JDK.

If you want to keep the standard API, you could write a simple utility method:

static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
    return first.isPresent() ? first : second;
}

and then:

Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));

If you have a lot of optionals to chains, maybe it's better to use the Stream approach as other mentionned already.

Solution 4 - Java

Based on Alexis C's answer, but without the nesting of orElses

String result = find1()
                   .map(Optional::of)
                   .orElseGet(Foo::find2())
                   .map(Optional::of)
                   .orElseGet(Foo::find3())
                   .orElseThrow(() -> new WhatEverException())

Remove the orElseThrow if you want an Optional<String> as the result instead.

The trick is to wrap each optional returned by the findX to another optional before each orElseGet.

Solution 5 - Java

// Java 9+
find1().or(() -> find2()).or(() -> find3());


// Java 8
Optional.ofNullable(
   find1().orElse(
      find2().orElse(
         find3().orElse( null )
)));

Solution 6 - Java

for cascading chaining You could use ifPresentOrElse

find1().ifPresentOrElse( System.out::println, new Runnable() {
  public void run() {
    find2().ifPresentOrElse( System.out::println, new Runnable() {
      public void run() {
        find3().ifPresentOrElse( System.out::println, new Runnable() {
          public void run() {
            System.err.println( "nothing found…" );
          }
        } );
      }
    } );
  }
} );

to do something with the value of the Optional You had to replace the System.out::println with Your Consumer
(different Consumers would also be possible in this solution)

Solution 7 - Java

My common generic solution for all those issues:

public static <T> T firstMatch(final Predicate<T> matcher, final T orElse, final T... values) {
  for (T t : values) {
    if (matcher.test(t)) {
      return t;
    }
  }
  return orElse;
}

Then you can do:

public static <T> Optional<T> firstPresent(final Optional<T>... values) {
  return firstMatch(Optional::isPresent, Optional.empty(), values);
}

Solution 8 - Java

To perform Optional Chaining First convert Stream to Optional Using either of the two methods

  1. findAny() or findFirst()
  2. min() / max()

Once optional is obtained optional has two more instance method which are also present in Stream class i.e filter and map(). use these on methods and to check output use ifPresent(System.out :: Println)

ex:

Stream s = Stream.of(1,2,3,4);

s.findFirst().filter((a)->a+1).ifPresent(System.out :: Println)

Output is : 2

Solution 9 - Java

Maybe one of

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, @SuppressWarnings("unchecked") Supplier<Optional<? extends T>>... supp) {
        if (first.isPresent()) return first;
        for (Supplier<Optional <? extends T>> sup : supp) {
            Optional<? extends T> opt = sup.get();
            if (opt.isPresent()) {
                return opt;
            }
        }
        return Optional.empty();
    }

    public <T> Optional<? extends T> firstOf(Optional<? extends T> first, Stream<Supplier<Optional<? extends T>>> supp) {
        if (first.isPresent()) return first;
        Stream<Optional<? extends T>> present = supp.map(Supplier::get).filter(Optional::isPresent);
        return present.findFirst().orElseGet(Optional::empty);
    }

will do.

The first one iterates over an array of suppliers. The first non-empty Optional<> is returned. If we don't find one, we return an empty Optional.

The second one does the same with a Stream of Suppliers which is traversed, each one asked (lazily) for their value, which is then filtered for empty Optionals. The first non-empty one is returned, or if no such exists, an empty one.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpilerView Question on Stackoverflow
Solution 1 - JavaSauli TähkäpääView Answer on Stackoverflow
Solution 2 - JavaIndrek OtsView Answer on Stackoverflow
Solution 3 - JavaAlexis C.View Answer on Stackoverflow
Solution 4 - JavaSweeperView Answer on Stackoverflow
Solution 5 - JavaMaster DroolsView Answer on Stackoverflow
Solution 6 - JavaKaplanView Answer on Stackoverflow
Solution 7 - JavaTwoTheView Answer on Stackoverflow
Solution 8 - JavaAdit chandelView Answer on Stackoverflow
Solution 9 - JavaglglglView Answer on Stackoverflow