可选orElse Java中的可选项

Yon*_*ree 128 java lambda optional java-8 java-9

我一直在使用Java 8中的新Optional类型,我遇到了似乎是功能上不支持的常见操作:"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);
}
Run Code Online (Sandbox Code Playgroud)

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

它的实现看起来像这样:

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

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

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

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

Nic*_*lai 79

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

return serviceA(args)
    .or(() -> serviceB(args))
    .or(() -> serviceC(args));
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅我写的Javadoc这篇文章.

  • 好的。这个添加肯定是一年前的事了,我没有注意到。关于您博客中的问题,更改返回类型会破坏二进制兼容性,因为字节码调用指令引用完整签名,包括返回类型,因此没有机会更改“ifPresent”的返回类型。但无论如何,我认为“ifPresent”这个名字无论如何都不是一个好名字。对于名称中不带有“else”的所有其他方法(例如“map”、“filter”、“flatMap”),这意味着如果不存在任何值,它们将不执行任何操作,那么为什么要使用“ifPresent”…… (2认同)

Hol*_*ger 60

鉴于当前的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();
Run Code Online (Sandbox Code Playgroud)

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

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

  • 只是在项目中使用它,感谢上帝,我们不做代码审查. (12认同)
  • 这当然是正确的......但说实话,它需要我一秒钟来解析它,我不相信这是正确的.请注意,我意识到这个例子是,但我可以想象一个小的变化,它不会被懒惰地评估,或者有一些其他错误,一目了然是难以区分的.对我而言,这将成为一个体面的实用功能类别. (4认同)
  • 它比"orElseGet"链更清洁,但阅读起来也困难得多. (3认同)
  • 有趣的是,几个月前我发布了一个非常类似的[解决方案](/sf/answers/2968401901/).这是我第一次遇到这个. (3认同)
  • 我想知道是否将`.map(Optional :: get)`与.findFirst()`切换会使其更易于“读取”,例如`.filter(Optional :: isPresent).findFirst()。map(Optional ::可以像“在流中找到Optional :: isPresent为true的第一个元素,然后通过应用Optional :: get展平它”那样“读取”? (2认同)
  • @schatten:此更改将使阅读此特定用例更加直观,但通常,在使用流时,您最好习惯这样一个事实,即像 `findFirst()` 这样的操作是终端操作,出现在流链,但暗示“查找第一个匹配元素”,无论最后一个 `filter` 和 `findFirst` 之间有多少其他中间操作。 (2认同)
  • @nullpointer 实际上,这种差异并不是很大。该 `List` 只是为 varargs 参数(在两个版本中都存在)创建的数组的一个薄包装器。当您对其调用 `stream()` 时,生成的流将基于相同类型的拆分器,当您有多个元素时,`Stream.of()` 将使用相同类型的拆分器,因此从那里开始,性能将相同。 (2认同)

Mis*_*sha 32

它不漂亮,但这将工作:

return serviceA(args)
  .map(Optional::of).orElseGet(() -> serviceB(args))
  .map(Optional::of).orElseGet(() -> serviceC(args))
  .map(Optional::of).orElseGet(() -> serviceD(args));
Run Code Online (Sandbox Code Playgroud)

.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)


aio*_*obe 26

也许这就是你所追求的:从一个或另一个获得价值

否则,你可能想看看Optional.orElseGet.这是我认为你所追求的一个例子:

result = Optional.ofNullable(serviceA().orElseGet(
                                 () -> serviceB().orElseGet(
                                     () -> serviceC().orElse(null))));
Run Code Online (Sandbox Code Playgroud)

  • 这就是天才通常会做的事情.将可选项评估为可空,并使用`ofNullable`包装它是我见过的最酷的东西. (2认同)

She*_*epy 6

假设您仍在使用 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以:

return Optionals.or(
   ()-> serviceA(args),
   ()-> serviceB(args),
   ()-> serviceC(args),
   ()-> serviceD(args)
);
Run Code Online (Sandbox Code Playgroud)

选项#2:使用图书馆

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

return serviceA(args)
  .or(() -> serviceB(args))
  .or(() -> serviceC(args))
  .or(() -> serviceD(args));
Run Code Online (Sandbox Code Playgroud)

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