可选orElse Java中的可选项

时间:2015-03-02 20:03:48

标签: java lambda java-8 optional java-9

我一直在使用新的Optional type in Java 8,而且我遇到了一些似乎不能在功能上支持的常见操作:" orElseOptional&#34 ;

考虑以下模式:

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

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

它的实现如下:

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

我很好奇是否存在这样一种方法不存在的原因,如果我只是以非预期的方式使用Optional,以及人们提出的其他方式处理这个案子。

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

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

6 个答案:

答案 0 :(得分:73)

这是JDK 9的一部分,格式为MyTask myTask = new MyTask(); String[] args = {"Hello"}; myTask.execute(args); ,需要or。那么你的例子就是:

Supplier<Optional<T>>

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

答案 1 :(得分:58)

鉴于当前的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();

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

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

答案 2 :(得分:30)

它不漂亮,但这会奏效:

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一起使用的相当方便的模式。这意味着&#34;如果此Optional包含值v,请给我func(v),否则请给我sup.get()&#34;。

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

此模式的其他用途是

  • .map(Stream::of).orElseGet(Stream::empty)
  • .map(Collections::singleton).orElseGet(Collections::emptySet)

答案 3 :(得分:24)

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

否则,您可能需要查看Optional.orElseGet。以下是我认为之后的一个例子:

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

答案 4 :(得分:4)

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

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

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

所以你可以这样做:

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

选项#2:使用库

E.g。 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)。

答案 5 :(得分:2)

这看起来非常适合模式匹配和更具传统性的Option接口,包含Some和None实现(例如JavaslangFunctionalJava中的那些)或lazy Maybe实现在cyclops-react。我是这个图书馆的作者。

使用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)));