144

I've been working with the new Optional type in Java 8, and I've come across what seems like a common operation that isn't supported functionally: an "orElseOptional"

Consider the following pattern:

Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
    Optional<Result> resultFromServiceB = serviceB(args);
    if (resultFromServiceB.isPresent) return resultFromServiceB;
    else return serviceC(args);
}

There are many forms of this pattern, but it boils down to wanting an "orElse" on an optional that takes a function producing a new optional, called only if the current one does not exist.

It's implementation would look like this:

public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
    return value != null ? this : other.get();
}

I'm curious if there's a reason such a method doesn't exist, if I'm just using Optional in an unintended way, and what other ways people have come up with to deal with this case.

I should say that I think that solutions involving custom utility classes/methods aren't elegant because people working with my code won't necessarily know they exist.

Also, if anyone knows, will such a method be included in JDK 9, and where might I propose such a method? This seems like a pretty glaring omission to the API to me.

Naman
  • 23,555
  • 22
  • 173
  • 290
Yona Appletree
  • 7,618
  • 6
  • 31
  • 44
  • 13
    [See this issue](https://bugs.openjdk.java.net/browse/JDK-8071670?page=com.atlassian.streams.streams-jira-plugin:activity-stream-issue-tab). To clarify: this is already going to be in Java 9 - if not in a future update of Java 8. – Obicere Mar 02 '15 at 20:05
  • It is! Thanks, didn't find that in my searching. – Yona Appletree Mar 02 '15 at 20:11
  • 2
    @Obicere That issue does not apply here because it is about _behavior_ on empty Optional, not about _an alternative result_. Optional already has `orElseGet()` for what OP needs, only it doesn't generate nice cascading syntax. – Marko Topolnik Sep 18 '15 at 11:28
  • 1
    http://stackoverflow.com/questions/23773024/functional-style-of-java-8s-optional-ifpresent-and-if-not-present/38282460#38282460 – Alessandro Giusa Jul 09 '16 at 13:54
  • I wrote [an answer](http://stackoverflow.com/a/38560612/2525313) using the new Java 9 syntax. – Nicolai Parlog Aug 10 '16 at 08:41
  • 1
    Great tutorials on Java Optional: http://codeflex.co/java-optional-no-more-nullpointerexception/ – John Detroit Apr 29 '18 at 08:51

6 Answers6

96

This is part of JDK 9 in the form of or, which takes a Supplier<Optional<T>>. Your example would then be:

return serviceA(args)
    .or(() -> serviceB(args))
    .or(() -> serviceC(args));

For details see the Javadoc or this post I wrote.

Andy Guibert
  • 34,857
  • 7
  • 32
  • 54
Nicolai Parlog
  • 36,673
  • 16
  • 109
  • 236
  • 1
    Nice. That addition must be a year old and I didn’t notice. Regarding the question in your blog, changing the return type would break the binary compatibility, as bytecode invocation instructions refer to the full signature, including the return type, so there’s no chance to change the return type of `ifPresent`. But anyway, I think the name `ifPresent` is not a good one anyway. For all other methods not bearing “else” in the name (like `map`, `filter`, `flatMap`), it is implied that they do nothing if no value is present, so why should `ifPresent`… – Holger Nov 04 '16 at 09:13
  • 1
    So adding an `Optional perform(Consumer c)` method to allow chaining `perform(x).orElseDo(y)` (`orElseDo` as an alternative to your proposed `ifEmpty`, to be consistent in have `else` in the name of all method that might do something for absent values). You can mimic that `perform` in Java 9 via `stream().peek(x).findFirst()` though that’s an abuse of the API and there’s still no way to execute a `Runnable` without specifying a `Consumer` at the same time… – Holger Nov 04 '16 at 09:21
66

The cleanest “try services” approach given the current API would be:

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
    ()->serviceA(args), 
    ()->serviceB(args), 
    ()->serviceC(args), 
    ()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();

The important aspect is not the (constant) chain of operations you have to write once but how easy it is to add another service (or modify the list of services in general). Here, adding or removing a single ()->serviceX(args) is enough.

Due to the lazy evaluation of streams, no service will be invoked if a preceding service returned a non-empty Optional.


Starting with Java 9, you can simplify the code to

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
    ()->serviceA(args), 
    ()->serviceB(args), 
    ()->serviceC(args), 
    ()->serviceD(args))
.flatMap(s -> s.get().stream())
.findFirst();

though this answer already contains an even simpler approach for JDK 9.

JDK 16 offers the alternative

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
    ()->serviceA(args), 
    ()->serviceB(args), 
    ()->serviceC(args), 
    ()->serviceD(args))
.<Result>mapMulti((s,c) -> s.get().ifPresent(c))
.findFirst();

though this approach might be more convenient with service methods accepting a Consumer rather than returning a Supplier.

Holger
  • 243,335
  • 30
  • 362
  • 661
  • 13
    just used that in a project, thank god we don't do code reviews though. – Ilya Smagin Jul 02 '15 at 11:24
  • 3
    It's a lot cleaner, than a chain of "orElseGet", but it is also a lot harder to read. – slartidan Sep 21 '15 at 20:27
  • 4
    This is certainly correct... but honestly, it takes me a second to parse it and I don't come away with confidence that it's correct. Mind you, I realize that this example is, but I could imagine a small change that would make it not be lazily evaluated or have some other error that would be indistinguishable at a glance. To me, this falls into would-be-decent-as-a-utility-function category. – Yona Appletree Oct 28 '15 at 07:40
  • 3
    I wonder if switching `.map(Optional::get)` with `.findFirst()` will make it easier to "read", e.g. `.filter(Optional::isPresent).findFirst().map(Optional::get)` can be "read" like "find the first element in the stream for which Optional::isPresent is true and then flatten it by applying Optional::get" ? – schatten Sep 28 '16 at 23:02
  • 2
    @schatten: this change would make reading this specific use case more intuitive, but generally, when working with streams, you’d better get used to the fact that ops like `findFirst()` are terminal operations, appearing at the end of the stream chain, but implying “find first matching element”, regardless of how many other intermediate operations are between the last `filter` and `findFirst`. – Holger Sep 29 '16 at 08:06
  • 1
    If you're on **Java 9**, see [my answer](http://stackoverflow.com/a/38560612/2525313). – Nicolai Parlog Nov 04 '16 at 07:37
  • 3
    Funny, I posted a very similar [solution](https://stackoverflow.com/a/42405741/1553851) for a similar question a few months ago. This is the first time I'm coming across this. – shmosel May 25 '17 at 06:50
  • I ended up listing almost a [similar solution](https://stackoverflow.com/a/53817936/1746118), yet inefficient I believe, in that since I started off creating a `List` of those suppliers first, instead of simply using the Stream right away. That typecast was way above me mostly. – Naman Dec 17 '18 at 15:23
  • 2
    @nullpointer actually, that difference is not very big. That `List` is just a thin wrapper around an array created for the varargs parameter (which exists in both versions). When you call `stream()` on it, the resulting stream will be based on the same type of spliterator that will be used by `Stream.of()` when you have more than one element, so from there, the performance will be identical. – Holger Dec 17 '18 at 16:31
36

It's not pretty, but this will work:

return serviceA(args)
  .map(Optional::of).orElseGet(() -> serviceB(args))
  .map(Optional::of).orElseGet(() -> serviceC(args))
  .map(Optional::of).orElseGet(() -> serviceD(args));

.map(func).orElseGet(sup) is a fairly handy pattern for use with Optional. It means "If this Optional contains value v, give me func(v), otherwise give me sup.get()".

In this case, we call serviceA(args) and get an Optional<Result>. If that Optional contains value v, we want to get Optional.of(v), but if it is empty, we want to get serviceB(args). Rinse-repeat with more alternatives.

Other uses of this pattern are

  • .map(Stream::of).orElseGet(Stream::empty)
  • .map(Collections::singleton).orElseGet(Collections::emptySet)
Misha
  • 25,007
  • 4
  • 52
  • 71
  • 1
    huh, when I use this strategy, eclipse says: "The method orElseGet(Supplier extends String>) in the type Optional is not applicable for the arguments (() -> {})" It doesn't seem to consider returning an Optional a valid strategy, on the String?? – chrismarx Sep 23 '16 at 20:00
  • @chrismarx `() -> {}` does not return an `Optional`. What are you trying to accomplish? – Misha Sep 23 '16 at 20:48
  • 1
    I'm just trying to follow the example. Chaining the map calls isn't working. My services return strings, and of course there's no .map() option available then – chrismarx Sep 25 '16 at 15:14
  • 1
    Excellent readable alternative to the upcoming `or(Supplier>)` of Java 9 – David M. Dec 21 '17 at 17:17
  • How did this get so many upvotes? This is totally incorrect. This will always produce serviceA, even when it's `empty()`. And that's because `serviceA(args).map(Optional::of)` will never be empty for it to reach the next `orElseGet` (i.e. `of(empty())` is not `empty()`) – Sheepy Mar 08 '18 at 06:03
  • 1
    @Sheepy you are mistaken. `.map()` on an empty `Optional` will produce an empty `Optional`. – Misha Mar 08 '18 at 06:05
  • Oh my god, i beg ur pardon, I completely misread that, you're right sorry. Btw normally i'd have a helper method e.g. `static Optional or(Supplier...)`. Alternatively, guava's optional does support the proper way (i.e. `Optional.or(Supplier>)`) – Sheepy Mar 08 '18 at 23:38
29

Perhaps this is what you're after: Get value from one Optional or another

Otherwise, you may want to have a look at Optional.orElseGet. Here's an example of what I think that you're after:

result = Optional.ofNullable(serviceA().orElseGet(
                                 () -> serviceB().orElseGet(
                                     () -> serviceC().orElse(null))));
Community
  • 1
  • 1
aioobe
  • 383,660
  • 99
  • 774
  • 796
  • 2
    This is what geniuses usually do. Evaluate the optional as a nullable and wrapping it with `ofNullable` is the coolest thing I've ever seen. – Jin Kwon Oct 28 '16 at 02:38
6

Assuming you're still on JDK8, there are several options.

Option#1: make your own helper method

E.g.:

public class Optionals {
    static <T> Optional<T> or(Supplier<Optional<T>>... optionals) {
        return Arrays.stream(optionals)
                .map(Supplier::get)
                .filter(Optional::isPresent)
                .findFirst()
                .orElseGet(Optional::empty);
    }
}

So that you could do:

return Optionals.or(
   ()-> serviceA(args),
   ()-> serviceB(args),
   ()-> serviceC(args),
   ()-> serviceD(args)
);

Option#2: use a library

E.g. google guava's Optional supports a proper or() operation (just like JDK9), e.g.:

return serviceA(args)
  .or(() -> serviceB(args))
  .or(() -> serviceC(args))
  .or(() -> serviceD(args));

(Where each of the services returns com.google.common.base.Optional, rather than java.util.Optional).

Sheepy
  • 657
  • 9
  • 16
  • I did not find `Optional.or(Supplier>)` in the Guava docs. Do you have a link for that? – Tamas Hegedus Nov 12 '18 at 13:19
  • .orElseGet returns T but Optional::empty returns Optional. Optional.ofNullable(........orElse(null)) has the desired effect as described by @aioobe. – Miguel Pereira May 01 '19 at 17:47
  • @TamasHegedus You mean in option#1? It's a custom implementation which is on the snipped above it. ps: sorry for the late reply – Sheepy Aug 19 '19 at 06:11
  • @MiguelPereira .orElseGet in this case returns Optional because it's operating on Optional> – Sheepy Aug 19 '19 at 06:14
2

This is looks like a good fit for pattern matching and a more traditional Option interface with Some and None implementations (such as those in Javaslang, FunctionalJava) or a lazy Maybe implementation in cyclops-react.I'm the author of this library.

With cyclops-react you can also use structural pattern matching on JDK types. For Optional you can match on the present and absent cases via the visitor pattern. it would look something like this -

  import static com.aol.cyclops.Matchables.optional;

  optional(serviceA(args)).visit(some -> some , 
                                 () -> optional(serviceB(args)).visit(some -> some,
                                                                      () -> serviceC(args)));
John McClean
  • 4,767
  • 1
  • 20
  • 30