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

时间:2014-12-11 18:51:08

标签: exception java-8 completable-future

我遇到了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;
});

有什么理由说这应该发生吗?在我看来,这是相当令人惊讶的。

2 个答案:

答案 0 :(得分:28)

此行为is specified in the class documentation of CompletionStage (fourth bullet)

  

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

如果您认为自己想要知道您调用的阶段exceptionally是否失败,或者是其直接或间接的先决条件之一,那就不足为奇了。

答案 1 :(得分:7)

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

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