可选orElse在Java中是可选的

可选orElse在Java中是可选的,java,lambda,java-8,optional,java-9,Java,Lambda,Java 8,Optional,Java 9,我一直在使用新的,我遇到了一个似乎在功能上不受支持的常见操作:“orElseOptional” 考虑以下模式: Optional<Result> resultFromServiceA = serviceA(args); if (resultFromServiceA.isPresent) return result; else { Optional<Result> resultFromServiceB = serviceB(args); if (result

我一直在使用新的,我遇到了一个似乎在功能上不受支持的常见操作:“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);
}
可选结果fromservicea=serviceA(args);
if(resultFromServiceA.isPresent)返回结果;
否则{
可选结果fromserviceb=serviceB(args);
if(resultFromServiceB.isPresent)返回resultFromServiceB;
else返回服务c(args);
}
这种模式有多种形式,但归根结底,它需要一个可选的“orElse”,该可选的函数生成一个新的可选函数,只有在当前可选函数不存在时才调用

它的实现如下所示:

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

public-Optional-orElse(供应商也许这就是您想要的:

否则,您可能需要看一看。以下是我认为您所追求的一个示例:

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)
是一种非常方便的模式,可与
可选
一起使用。它的意思是“如果此
可选
包含值
v
,请给我
func(v)
,否则请给我
sup.get()

在这种情况下,我们调用
serviceA(args)
并得到一个
Optional
。如果
Optional
包含值
v
,我们想得到
Optional.of(v)
,但如果它是空的,我们想得到
serviceB(args)
。用更多的替代品重复

此模式的其他用途包括

  • .map(Stream::of).orElseGet(Stream::empty)
  • .map(Collections::singleton).orElseGet(Collections::emptySet)

鉴于当前的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();
尽管已经包含了一种更简单的JDK9方法

JDK16提供了另一种选择

可选o=Stream.of(
()->服务A(args),
()->服务B(args),
()->服务C(args),
()->已服务(args))
.mapMulti((s,c)->s.get().ifPresent(c))
.findFirst();

虽然这种方法可能更方便,因为服务方法接受
消费者
而不是返回
供应商

,但它看起来非常适合模式匹配,并且是一种更传统的选项接口,有一些实现,而没有实现(例如,)或者是中的一个懒惰的实现。我是这个库的作者

通过,您还可以在JDK类型上使用structural。对于可选的,您可以通过匹配当前和不存在的情况。它看起来像这样-

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

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

这是JDK 9的一部分,形式为
,它采用
供应商
。您的示例如下:

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

有关详细信息,请参阅或我写的。

假设您仍然使用JDK8,有几个选项

选项1:制作自己的助手方法 例如:

选项2:使用库 例如,google guava的可选支持适当的
或()
操作(就像JDK9),例如:


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

。澄清一下:这已经在Java 9中出现了-如果不是在Java 8的未来更新中的话。是的!谢谢,在我的搜索中没有发现。@Obicere这个问题不适用于这里,因为它是关于空可选的行为,而不是关于替代结果。可选已经有
orElseGet()
对于OP所需要的,只是它不能生成很好的级联语法。关于Java的优秀教程可选:只是在项目中使用了它,谢天谢地,我们不做代码审查。它比一系列“OrelGet”要干净得多,但它也很难阅读。这当然是正确的…但老实说,我花了一秒钟来解析它,我没有信心它是正确的。请注意,我意识到这个例子是正确的,但我可以想象一个小的变化,使它不会被懒散地评估或有一些其他错误,这将是无法区分的看一眼。对我来说,这属于一种实用功能。我想知道用
.map(可选::get)
切换
.findFirst()
是否会更容易“读取”,例如
.filter(可选::isPresent).findFirst().map(可选::get)
可以像这样“读取”“在流中找到Optional::isPresent为true的第一个元素,然后通过应用Optional::get将其展平”?有趣的是,我在几个月前发布了一个非常类似的问题。这是我第一次遇到这个问题。嗯,当我使用这个策略时,eclipse说:“方法orElseGet。”(Supplier@chrismarx
()->{}
不会返回一个
可选的
。你想做什么?我只是想按照下面的例子来做。链接映射调用不起作用。我的服务返回字符串,当然没有.map()选项可用,这是即将推出的
或(供应商)的最佳可读替代方案Java的
9@Sheepy你弄错了。
.map()
在一个空的
Optional
上将产生一个空的
Optional
。这是天才们通常做的事情。将可选项评估为空值,并用
of nullable
将其包装是我见过的最酷的事情。很好。这个添加一定有一年了,我没有注意到。关于你博客中的问题,更改返回类型将破坏二进制兼容性,因为字节码调用指令引用完整签名,包括返回类型,因此没有机会更改
ifPresent
的返回类型。但是无论如何,我认为名称
ifPresent
不是一个好名称。对于所有其他不带“else”的方法在名称中(如
map
filter
flatMap
),意味着如果不存在值,它们什么也不做,那么为什么
ifPresent
…那么添加一个
可选的perform(Consumer c)
方法来允许链接
perfo
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)
);
return serviceA(args)
  .or(() -> serviceB(args))
  .or(() -> serviceC(args))
  .or(() -> serviceD(args));