Java 8 CompletableFuture 异常方法的惊人行为

Posted

技术标签:

【中文标题】Java 8 CompletableFuture 异常方法的惊人行为【英文标题】:Surprising behavior of Java 8 CompletableFuture exceptionally method 【发布时间】:2015-02-10 08:58:28 【问题描述】:

我遇到了 Java 8 CompletableFuture.exceptionally 方法的奇怪行为。如果我执行此代码,它可以正常工作并打印java.lang.RuntimeException

CompletableFuture<String> future = new CompletableFuture<>();

future.completeExceptionally(new RuntimeException());

future.exceptionally(e -> 
            System.out.println(e.getClass());
            return null;
);

但是如果我在以后的处理中添加另一个步骤,例如thenApply,异常类型将更改为java.util.concurrent.CompletionException,其中包含原始异常。

CompletableFuture<String> future = new CompletableFuture<>();

future.completeExceptionally(new RuntimeException());

future.thenApply(v-> v).exceptionally(e -> 
            System.out.println(e);
            return null;
);

有什么理由会发生这种情况吗?在我看来,这很令人惊讶。

【问题讨论】:

查看 ***.com/q/49230980/14731 了解何时包装(或不包装)异常的概述。 这是我发现的一篇文章,帮助我更好地理解了这个问题的解决方案 -> millross-consultants.com/… @The0bserver,您添加的链接信息量很大,虽然不是全部,但我的很多疑问都清楚了:) 很高兴我能帮上忙@NIGAGA。 【参考方案1】:

这种行为is specified in the class documentation of CompletionStage (fourth bullet):

方法handle 还允许该阶段计算替换结果,该结果可能允许其他相关阶段进行进一步处理。在所有其他情况下,如果某个阶段的计算因(未经检查的)异常或错误而突然终止,则所有需要其完成的相关阶段也会异常完成,CompletionException 将异常作为其原因。

如果您考虑到您可能知道您调用 exceptionally 的阶段是否失败,或者它的直接或间接先决条件之一是否失败,这并不奇怪。

【讨论】:

【参考方案2】:

是的,该行为是预期的,但如果您想要从先前阶段之一抛出的原始异常,您可以简单地使用它

CompletableFuture<String> future = new CompletableFuture<>();

future.completeExceptionally(new RuntimeException());

future.thenApply(v-> v).exceptionally(e -> 
        System.out.println(e.getCause()); // returns a throwable back
        return null;
);

【讨论】:

以上是关于Java 8 CompletableFuture 异常方法的惊人行为的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 CompletableFuture 浅入

Java 8 CompletableFuture 与 Netty 未来

Java 8 的异步编程利器 CompletableFuture 详解

Java 8 的异步编程利器 CompletableFuture 详解

Java 8 的异步编程利器 CompletableFuture 详解

Java 8 的异步利器:CompletableFuture源码级解析(建议精读)