Java 中的可选项或其他可选项

我一直在使用新的 Java8中的可选类型,我遇到了一个似乎是常见的操作,它在功能上不受支持: “ orElseOptions”

考虑以下模式:

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

我很好奇这种方法不存在的原因,如果我只是无意中使用了可选项,以及人们还想出了什么其他方法来处理这种情况。

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

另外,如果有人知道,JDK9中是否包含这样的方法,我可以在哪里提出这样的方法?在我看来,这似乎是 API 的一个明显遗漏。

80971 次浏览

也许这就是你想要的: 从一个可选项或另一个可选项获取值

否则,你可能想看看 Optional.orElseGet。下面是你想要的 好好想想的一个例子:

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

虽然不怎么样,但是这个会有用的:

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)

考虑到当前的 API,最干净的“ try services”方法是:

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,则不会调用任何服务。


从 Java9开始,您可以将代码简化为

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.flatMap(s -> s.get().stream())
.findFirst();

尽管 这个答案已经为 JDK9提供了一种更简单的方法。

JDK 16提供了另一种选择

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.<Result>mapMulti((s,c) -> s.get().ifPresent(c))
.findFirst();

尽管这种方法对于接受 Consumer而不是返回 Supplier的服务方法可能更方便。

这看起来非常适合模式匹配和一个更传统的 Option 接口,它有 Some 和 None 实现(比如在 爪哇语FunctionalJava中的实现) ,或者在 剑水蚤-反应中有一个惰性的 也许吧实现。

使用 剑水蚤-反应,您还可以在 JDK 类型上使用结构化 模式匹配。对于可选项,您可以通过 访客模式匹配当前和缺席的情况。看起来像这样

  import static com.aol.cyclops.Matchables.optional;


optional(serviceA(args)).visit(some -> some ,
() -> optional(serviceB(args)).visit(some -> some,
() -> serviceC(args)));

这是 JDK 9中 or形式的一部分,它接受 Supplier<Optional<T>>:

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

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

假设您仍然使用 JDK8,有几种选择。

选项 # 1: 创建自己的 helper 方法

例如:

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: 使用库

谷歌番石榴的可选支持正确的 or()操作(就像 JDK9一样) ,例如:

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

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