Java 8中的链接选项

pil*_*ler 59 java lambda optional java-8

寻找一种链接选项的方法,以便返回第一个存在的选项.如果没有,Optional.empty()则应退回.

假设我有几个像这样的方法:

Optional<String> find1()
Run Code Online (Sandbox Code Playgroud)

我想把它们链起来:

Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );
Run Code Online (Sandbox Code Playgroud)

但当然这不起作用,因为orElse期望一个值,并orElseGet期望一个Supplier.

Sau*_*pää 80

使用流:

Stream.of(find1(), find2(), find3())
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();
Run Code Online (Sandbox Code Playgroud)

如果您需要懒惰地评估查找方法,请使用供应商功能:

Stream.of(this::find1, this::find2, this::find3)
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();
Run Code Online (Sandbox Code Playgroud)

  • 虽然它有很多代码可以解决一个简单的问题.让我觉得它应该是在Optional api中内置的 (6认同)
  • Optional中肯定缺少的一件事是转换为Stream,而不仅仅是针对这个用例.然后这将更易于管理:`Stream.of(opt1,opt2,opt3).flatMap(Optional :: stream).findFirst()`. (5认同)
  • 从带参数的方法中链接选项...... - &gt; http://stackoverflow.com/questions/28818506/java-8-optional-orelse-optional#answer-28833677 (2认同)

Ale*_* C. 33

你可以这样做:

Optional<String> resultOpt = Optional.of(find1()
                                .orElseGet(() -> find2()
                                .orElseGet(() -> find3()
                                .orElseThrow(() -> new WhatEverException()))));
Run Code Online (Sandbox Code Playgroud)

虽然我不确定它是否提高了IMO的可读性.Guava提供了一种链接Optionals的方法:

import com.google.common.base.Optional;

Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());
Run Code Online (Sandbox Code Playgroud)

它可能是您的问题的另一种选择,但不使用JDK中的标准Optional类.

如果要保留标准API,可以编写一个简单的实用程序方法:

static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
    return first.isPresent() ? first : second;
}
Run Code Online (Sandbox Code Playgroud)

然后:

Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));
Run Code Online (Sandbox Code Playgroud)

如果你有很多可选择的链,也许最好使用Stream方法,就像其他提到的那样.

  • `或(可选<T> ...选择)`将是一种改进.但是,带有泛型的Varargs是一个不幸的组合. (4认同)
  • 使用实用方法的解决方案可能具有可读性.太糟糕了java的Optional与Guava没有相同的方法 (3认同)
  • @zeroflagL是的,在这种情况下它将抛出一个新的WhatEverException(OP最初并不准确,在这种情况下他会有一个空的可选项).不确定你是否可以在一行中完成,但我不建议使用这种方法. (2认同)
  • 也许有一个误解.如果`find1`返回一个值(=非空`可选`),则`find3`的返回值无关紧要.但是在你的第一个解决方案中,尽管事实上我们已经有了一个有效的结果,但仍然会抛出异常.换句话说:理想情况下,首先不应该调用`find3`. (2认同)

Ind*_*Ots 27

受Sauli的回答启发,可以使用该flatMap()方法.

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
  .findFirst();
Run Code Online (Sandbox Code Playgroud)

将Optional转换为Stream非常麻烦.显然,这将由JDK9修复.所以这可以写成

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(Optional::stream)
  .findFirst();
Run Code Online (Sandbox Code Playgroud)

Java 9发布后更新

尽管最初的问题是关于Java 8的,但是Optional::or在Java 9中引入了它.有了它,问题可以解决如下

Optional<String> result = find1()
  .or(this::find2)
  .or(this::find3);
Run Code Online (Sandbox Code Playgroud)


epo*_*pox 17

从 Java 9 开始

读者正在寻找的最有可能的情况(今天)

result = find1()
    .or(this::find2)
    .or(this::find3);
Run Code Online (Sandbox Code Playgroud)

爪哇8

result = Optional.ofNullable(find1()
    .orElse(find2()
      .orElse(find3()
        .orElse(null))));
Run Code Online (Sandbox Code Playgroud)

性能:上述Java 8解决方案每次都会预先调用find2()find3() ,即使(稍后) find1()计算返回非空时也是如此。

更新:具有惰性求值的性能最佳Java 8解决方案(感谢 @Alexander 的评论)将是:

result = Optional.ofNullable(find1()
    .orElseGet(() -> find2()
      .orElseGet(() -> find3()
        .orElse(null))));      // <-- null would be the last resort, when all findN are empty.
                               //     #ofNullable (in the 1st line would wrap it to Optional
                               //     and return Optional.empty() as OP requires.
Run Code Online (Sandbox Code Playgroud)

  • 对于 java 8 这是错误的建议。所有可选值都会被评估,即使第一个具有不可为空的值。通常为空值 - 非法状态的标记,您应该抛出一个异常。检查它是如何工作的,例如: `String value =Optional.ofNullable("str").orElse(Optional.&lt;String&gt;ofNullable(null).orElseThrow(() -&gt; new IllegalStateException()));` 和正确的惰性求值的解决方案: ```String value =Optional.ofNullable("str").orElseGet(() -&gt;Optional.&lt;String&gt;ofNullable(null).orElseThrow(() -&gt; new IllegalStateException()));` `` (3认同)