我习惯了 ListenableFuture 模式,使用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);
  }
})

似乎Java 8的 CompletableFuture 旨在处理或多或少相同的用例。天真的,我可以将上面的示例翻译为:
CompletableFuture<String> future = CompletableFuture<String>.supplyAsync(...)
  .thenAccept(this::handleResult)
  .exceptionally((t) -> log.error("Unexpected error", t));

这肯定比ListenableFuture版本更详细,而且看起来很有希望。

但是,它不会编译,因为exceptionally()不采用Consumer<Throwable>,因此它采用Function<Throwable, ? extends T> -在这种情况下为Function<Throwable, ? extends String>

这意味着我不能只记录错误,我必须想出一个String值以在错误情况下返回,并且没有有意义的String值以在错误情况下返回。我可以返回null,仅用于编译代码:
  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return null; // hope this is ignored
  });

但这又开始变得冗长,除了冗长之外,我不喜欢让null随处可见-这表明有人可能会尝试检索或捕获该值,并且在以后的某个时刻,我可能会遇到意外NullPointerException

如果exceptionally()接受了Function<Throwable, Supplier<T>>,我至少可以做这样的事情-
  .exceptionally((t) -> {
    log.error("Unexpected error", t);
    return () -> {
      throw new IllegalStateException("why are you invoking this?");
    }
  });

-但事实并非如此。

exceptionally()永远不会产生有效值时,该怎么办?我可以使用CompletableFuture进行其他操作,或者使用新Java 8库中的其他方法来更好地支持此用例吗?

最佳答案

正确的CompletableFuture对应转换是:

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

这里有趣的部分是您在示例中链接了 future 。看似流利的语法实际上是在链接 future ,但似乎您不希望在这里。

如果您想返回一个处理带有内部 future 结果的东西的 future ,则whenComplete返回的 future 可能会很有趣。它保留当前将来的异常(exception)(如果有)。但是,如果将来正常完成并且引发续集,那么它将以异常抛出异常完成。

区别在于future完成后发生的所有事情都将在下一个继续之前发生。如果您是exceptionally的最终用户,则使用thenAcceptfuture是等效的,但是如果要向调用者提供将来的服务,则任何一个都将在没有完成通知的情况下进行处理(就像在后台,如果可能的话) ,很可能是exceptionally延续,因为您可能希望异常在进一步的延续上级联。

10-07 22:43