我遇到了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;
});
有什么理由说这应该发生吗?在我看来,这是相当令人惊讶的。
答案 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;
});