Chaining Optionals in Java 8

前端 未结 5 957
刺人心
刺人心 2020-11-29 01:48

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.

Assumi

相关标签:
5条回答
  • 2020-11-29 02:14

    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.

    0 讨论(0)
  • 2020-11-29 02:15

    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

    0 讨论(0)
  • 2020-11-29 02:23

    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();
    
    0 讨论(0)
  • 2020-11-29 02:23

    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.

    0 讨论(0)
  • 2020-11-29 02:27

    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);
    
    0 讨论(0)
提交回复
热议问题