首页 文章

可选orElse Java中的可选项

提问于
浏览
118

我一直在使用新的Optional type in Java 8,并在功能上支持我've come across what seems like a common operation that isn':"orElseOptional"

考虑以下模式:

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);
}

这种模式有很多种形式,但归结为在一个可选项上需要一个“orElse”,它接受一个生成一个新的可选项的函数,仅当当前的一个不存在时才被调用 .

它的实现看起来像这样:

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

我很好奇是否有这样的方法不存在的原因,如果我只是以一种无意的方式使用Optional,以及人们提出了处理这种情况的其他方式 .

我应该说,我认为涉及自定义实用程序类/方法的解决方案并不优雅,因为使用我的代码的人不一定知道它们存在 .

另外,如果有人知道,这样的方法是否会包含在JDK 9中,我可以在哪里提出这样的方法?对我来说,这似乎是对API的一个相当明显的遗漏 .

6 回答

  • 22

    这是JDK 9的一部分,形式为 or ,需要 Supplier<Optional<T>> . 那么你的例子就是:

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

    有关详细信息,请参阅the Javadocthis post我写道 .

  • 56

    鉴于当前的API,最干净的“尝试服务”方法将是:

    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();
    

    重要的方面不是您必须编写一次的(常量)操作链,而是添加其他服务(或修改服务列表)是多么容易 . 在这里,添加或删除单个_588348就足够了 .

    由于对流的延迟评估,如果前面的服务返回非空 Optional ,则不会调用任何服务 .

  • 70

    它不漂亮,但这将工作:

    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) 是一个非常方便的模式,可用于 Optional . 这意味着“如果 Optional 包含值 v ,请给我 func(v) ,否则给我 sup.get() ” .

    在这种情况下,我们调用 serviceA(args) 并获得 Optional<Result> . 如果 Optional 包含值 v ,我们想得到 Optional.of(v) ,但如果它是空的,我们想得到 serviceB(args) . 用更多替代品冲洗重复 .

    这种模式的其他用途是

    • .map(Stream::of).orElseGet(Stream::empty)

    • .map(Collections::singleton).orElseGet(Collections::emptySet)

  • 28

    也许这就是你所追求的:Get value from one Optional or another

    否则,您可能需要查看Optional.orElseGet . 以下是我认为你所追求的一个例子:

    result = Optional.ofNullable(serviceA().orElseGet(
                                     () -> serviceB().orElseGet(
                                         () -> serviceC().orElse(null))));
    
  • 3

    假设你还在JDK8上,有几种选择 .

    选项#1:制作自己的帮助方法

    例如 . :

    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);
        }
    }
    

    这样你就可以做到:

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

    选项#2:使用库

    例如 . google guava的Optional支持正确的 or() 操作(就像JDK9一样),例如:

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

    (其中每个服务返回 com.google.common.base.Optional ,而不是 java.util.Optional ) .

  • 2

    这看起来非常适合模式匹配和更传统的Option接口与Some和None实现(例如JavaslangFunctionalJava中的那些)或cyclops-react中的懒惰Maybe实现 . 我是这个库的作者 .

    使用cyclops-react,您还可以在JDK类型上使用结构pattern matching . 对于Optional,您可以通过visitor pattern匹配当前和不存在的情况 . 看起来像这样 -

    import static com.aol.cyclops.Matchables.optional;
    
      optional(serviceA(args)).visit(some -> some , 
                                     () -> optional(serviceB(args)).visit(some -> some,
                                                                          () -> serviceC(args)));
    

相关问题