【问题标题】:What's the most elegant way to combine optionals?组合选项最优雅的方式是什么?
【发布时间】:2015-09-25 07:11:51
【问题描述】:

这是我目前所得到的:

Optional<Foo> firstChoice = firstChoice();
Optional<Foo> secondChoice = secondChoice();
return Optional.ofNullable(firstChoice.orElse(secondChoice.orElse(null)));

这让我觉得既可怕又浪费。如果 firstChoice 存在,我会不必要地计算 secondChoice。

还有一个更高效的版本:

Optional<Foo> firstChoice = firstChoice();
if(firstChoice.isPresent()) {
 return firstChoice;
} else {
 return secondChoice();
}

在这里,如果不复制映射器或声明另一个局部变量,我就无法将某些映射函数链接到末尾。所有这些都使得代码比要解决的实际问题更复杂。

我宁愿写这个:

return firstChoice().alternatively(secondChoice());

但是 Optional::alternatively 显然不存在。现在呢?

【问题讨论】:

  • ...遇到空的问题...现在我们有两个问题。
  • 顺便说一句,Paul Sandoz 刚刚发布了一个 changeset 进行审查,其中介绍了 Optional.or() 方法,这正是您想要的:firstChoice().or(this::secondChoice)。更多信息请访问JDK-8080418。在 Java-9 时代,我们会很高兴!

标签: java java-8 optional


【解决方案1】:

试试这个:

firstChoice().map(Optional::of)
             .orElseGet(this::secondChoice);

map 方法为您提供Optional&lt;Optional&lt;Foo&gt;&gt;。然后,orElseGet 方法将其变平为Optional&lt;Foo&gt;secondChoice 方法只有在 firstChoice() 返回空的可选项时才会被计算。

【讨论】:

  • 注意:Java 9 引入了Optional::or 方法。因此,这可以在 Java 9 中写成firstChoice().or(this::secondChoice)
  • 这仅适用于 Java8,如果 this::secondChoice 返回 Supplier 而不是 Optional - 来自 OP 的示例没有。
  • @exception this::secondChoice Supplier。它是一个方法参考。
  • 在 OP 的示例中,该方法返回 Optional,它不是供应商 - 我指出 Java8 是不同的。
  • @exception secondChoice() 返回一个Optionalthis::secondChoiceSupplier&lt;Optional&lt;Foo&gt;&gt; 类型的方法引用。如果你调用方法引用,你会得到Optional。答案中的示例适用于 Java 8 及更高版本。
【解决方案2】:

可能是这样的:

Optional<String> finalChoice = Optional.ofNullable(firstChoice()
    .orElseGet(() -> secondChoice()
    .orElseGet(() -> null)));

发件人:Chaining Optionals in Java 8

【讨论】:

  • 你的三行的最后一行可以简化为:.orElse(null)));
  • 不确定在 Optional 中返回 null 是否是个好主意,因为它们是为了避免使用 null 而引入的。我宁愿返回 Optional.empty()
【解决方案3】:

您可以简单地将其替换为,

Optional<Foo> firstChoice = firstChoice();
return firstChoice.isPresent()? firstChoice : secondChoice();

除非 firstChoice.isPresent() 为 false,否则上述代码不会调用。

但是您必须准备好调用这两个函数以获得所需的输出。没有其他方法可以逃避检查。

  • 最好的情况是首选返回 true。
  • 最坏的情况是首选返回 false,因此另一种方法 呼吁第二选择。

【讨论】:

    【解决方案4】:

    这是@marstran 解决方案对于任意数量的选项的概括:

    @SafeVarargs
    public static <T> Optional<T> selectOptional(Supplier<Optional<T>>... optionals) {
        return Arrays.stream(optionals)
                .reduce((s1, s2) -> () -> s1.get().map(Optional::of).orElseGet(s2))
                .orElse(Optional::empty).get();
    }
    

    测试:

    public static Optional<String> first() {
        System.out.println("foo called");
        return Optional.empty();
    }
    
    public static Optional<String> second() {
        System.out.println("bar called");
        return Optional.of("bar");
    }
    
    public static Optional<String> third() {
        System.out.println("baz called");
        return Optional.of("baz");
    }
    
    public static void main(String[] args) {
        System.out.println(selectOptional(() -> first(), () -> second(), () -> third()));
    }
    

    输出:

    foo called
    bar called
    Optional[bar]
    

    【讨论】:

      【解决方案5】:

      Java 8 不支持这一点让我感到非常沮丧,因此我切换回了具有 or 的 guava 选项:

      public abstract Optional<T> or(Optional<? extends T> secondChoice)
      

      如果存在值,则返回此 Optional;否则选择第二个。

      【讨论】:

        【解决方案6】:

        惰性计算和任意数量的Optional 元素

        Stream.<Supplier<Optional<Foo>>>of(
                this::firstChoice,
                this::secondChoice
        ).map(
                Supplier::get
        ).filter(
                Optional::isPresent
        ).findFirst(
        ).orElseGet(
            Optional::empty
        );
        

        【讨论】:

          【解决方案7】:

          Java 9 针对这种情况添加了Optional.or​(Supplier&lt;? extends Optional&lt;? extends T&gt;&gt; supplier) 方法。

          给定方法firstChoice()secondChoice(),每个都返回Optional&lt;Foo&gt;,下面的单行代码使用Optional.or 来达到预期的结果:

          return firstChoice().or(this::secondChoice);
          

          这种方法的额外好处是,当firstChoice 为空时,只计算secondChoice

          【讨论】:

          • 这应该是公认的答案。
          【解决方案8】:

          这是一种适用于基于流 API 的任意数量的Optional 的方法:

          return Arrays.asList(firstChoice, secondChoice).stream()
            .filter(Optional::isPresent)
            .map(Optional::get)
            .findFirst().orElse(null);
          

          这不是最短的。但更通俗易懂。

          如果您已经在使用其中一个库,另一种方法是使用来自 commons-lang 的 Guava 的 firstNonNull()

          firstNonNull(firstChoice.orElse(null), secondChoice.orElse(null));
          

          【讨论】:

          • 我发现这比问题中的if 解决方案更难理解,顺便说一句。
          • 好吧,如果你只有 2 个Optionals,我同意。这就是为什么我说这种方式适用于Optionals的任意数
          • 您的解决方案的问题在于它不是短路的:您必须评估所有选择,这正是 OP 试图避免的。在 OPs 代码中,firstChoice()secondChoice() 实际上是方法,如果第一个产生非空可选,您不能简单地修改您的解决方案以省略调用第二个。
          • 什么意思? findFirst() 在文档中定义为短路操作。您可以使用调试器轻松检查 Optional.get()Optional.isPresent() 方法仅调用一次。
          猜你喜欢
          • 1970-01-01
          • 2010-11-04
          • 2010-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-28
          • 1970-01-01
          相关资源
          最近更新 更多