Java 传递给CompletableFuture.Exceptional()的异常处理程序是否必须返回有意义的值?

Java 传递给CompletableFuture.Exceptional()的异常处理程序是否必须返回有意义的值?,java,completable-future,Java,Completable Future,我已经习惯了这种模式,比如onSuccess()和onFailure()回调 ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); ListenableFuture<String> future = service.submit(...) Futures.addCallback(future, new FutureCallback<

我已经习惯了这种模式,比如
onSuccess()
onFailure()
回调

ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
ListenableFuture<String> future = service.submit(...)
Futures.addCallback(future, new FutureCallback<String>() {
  public void onSuccess(String result) {
    handleResult(result);
  }
  public void onFailure(Throwable t) {
    log.error("Unexpected error", t);
  }
})
这当然比
ListenableFuture
版本更详细,看起来非常有希望

但是,它不编译,因为
异常()
不接受
消费者
,它接受
函数
——在本例中,是
函数

这意味着我不能只记录错误,我必须在错误情况下返回
字符串
值,并且在错误情况下没有有意义的
字符串
值可返回。我可以返回
null
,只是为了得到要编译的代码:

  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return null; // hope this is ignored
  });
但这又开始变得冗长,除了冗长之外,我不喜欢让
null
四处浮动——这意味着有人可能试图检索或捕获该值,而在很久以后的某个时候,我可能会遇到意外的
NullPointerException

如果
异常()
使用
函数
我至少可以这样做--

--但事实并非如此


异常(
不应产生有效值时,正确的做法是什么?我是否可以用
CompletableFuture
或新的Java 8库中的其他东西来更好地支持此用例?

注意,
异常(函数fn)
也会返回
CompletableFuture
。所以你可以更进一步

函数的返回值
用于为下一个链式方法生成回退结果。因此,您可以从缓存中获取该值,如果该值在DB中不可用

CompletableFuture<String> future = CompletableFuture<String>.supplyAsync(/*get from DB*/)
  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return "Fallback value from cache";
  })
  .thenAccept(this::handleResult);

CompletableFuture
的正确对应转换为:

CompletableFuture<String> future = CompletableFuture.supplyAsync(...);
future.thenAccept(this::handleResult);
future.exceptionally(t -> {
    log.error("Unexpected error", t);
    return null;
});
CompletableFuture=CompletableFuture.supplyAsync(…);
然后接受(这个::handleResult);
未来。例外(t->{
日志错误(“意外错误”,t);
返回null;
});
另一种方式:

CompletableFuture<String> future = CompletableFuture.supplyAsync(...);
future
    .whenComplete((r, t) -> {
        if (t != null) {
            log.error("Unexpected error", t);
        }
        else {
            this.handleResult(r);
        }
    });
CompletableFuture=CompletableFuture.supplyAsync(…);
未来
.完成时((r,t)->{
如果(t!=null){
日志错误(“意外错误”,t);
}
否则{
这是handleResult(r);
}
});
有趣的是,您在示例中链接了未来。看似流畅的语法实际上是链接未来,但似乎您不希望出现这种情况

如果您想要返回一个用内部未来的结果处理某件事情的未来,那么
whenComplete
返回的未来可能会很有趣。它保留当前未来的异常(如果有)。但是,如果future正常完成,而continuation抛出,那么它将异常完成,抛出异常


不同之处在于,在
future
完成之后发生的任何事情都将在下一次继续之前发生。如果您是
未来的
的最终用户,则使用
异常
接受
是等效的,但是如果您向调用者提供未来,则其中任何一个都将在没有完成通知的情况下进行处理(如果可以的话,就像在后台一样),最有可能的是
异常
继续,因为您可能希望在进一步的继续中级联异常。

不幸的是,第一个没有实际编译,因为
log.error(…)
void
并且
异常()
需要返回未来返回类型的内容(在本例中是一个
字符串
)(这从根本上引出了我的问题。)不过,感谢您对fluent API和链接的评论。
CompletableFuture<String>.supplyAsync(/*get from DB*/)
  .thenAccept(this::handleResult)
  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return null;
  });
CompletableFuture<String> future = CompletableFuture.supplyAsync(...);
future.thenAccept(this::handleResult);
future.exceptionally(t -> {
    log.error("Unexpected error", t);
    return null;
});
CompletableFuture<String> future = CompletableFuture.supplyAsync(...);
future
    .whenComplete((r, t) -> {
        if (t != null) {
            log.error("Unexpected error", t);
        }
        else {
            this.handleResult(r);
        }
    });