46

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

有什么理由应该发生这种情况吗?在我看来,这非常令人惊讶。

4

2 回答 2

35

此行为在(第四个项目符号)的类文档中指定CompletionStage

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

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

于 2014-12-11T19:24:14.757 回答
6

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

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;
});
于 2017-06-17T06:42:00.050 回答