可选或Java中的其他可选

vmdwslir  于 2023-01-11  发布在  Java
关注(0)|答案(6)|浏览(95)

我一直在使用新的Optional type in Java 8,我遇到了一个功能上不支持的常见操作:“或其他可选”
请考虑以下模式:

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

我很好奇这样的方法不存在是否有原因,我是否只是无意中使用了Optional,以及人们还想出了什么其他方法来处理这种情况。
我应该说,我认为涉及自定义实用程序类/方法的解决方案并不优雅,因为使用我的代码的人不一定知道它们的存在。
另外,如果有人知道,这样的方法会包含在JDK 9中吗?我可以在哪里提出这样的方法?在我看来,这似乎是API中一个非常明显的遗漏。

rjzwgtxy

rjzwgtxy1#

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

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

有关详细信息,请参阅Javadoc、我写的这篇文章或介绍此方法的ticket JDK-8080418

b4lqfgs4

b4lqfgs42#

给定当前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,则不会调用任何服务。
从Java9开始,您可以将代码简化为

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

尽管this answer已经包含了一种更简单的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的服务方法可能更方便。

9w11ddsr

9w11ddsr3#

不是很漂亮,但这会有用的:

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包含值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)
brvekthn

brvekthn4#

也许这就是你想要的Get value from one Optional or another
否则,你可能想看看Optional.orElseGet。下面是一个我认为你在寻找的例子:

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

r3i60tvu5#

假设您还在使用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);
    }
}

这样你就可以:

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

选项2:使用图书馆

例如,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)。

kqlmhetl

kqlmhetl6#

这看起来很适合模式匹配和更传统的带有Some和None实现的Option接口(如JavaslangFunctionalJava中的那些)或cyclops-react中的lazy Maybe实现。
对于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)));

相关问题