Does CompletionStage always wrap exceptions in CompletionException?

≯℡__Kan透↙ 提交于 2019-12-17 16:16:54

问题


The CompletionStage Javadoc states:

[...] if a stage's computation terminates abruptly with an (unchecked) exception or error, then all dependent stages requiring its completion complete exceptionally as well, with a CompletionException holding the exception as its cause.

Seeing as exceptional completions always wrap exceptions in CompletionException why do exceptionally(), whenComplete() and handle() represent the exception as Throwable instead of CompletionException?

This matters because it prevents one from directly re-throwing exceptions inside these methods.

Is it possible for these methods to receive an exception other than CompletionException? Or can I safely force a cast to this type?

(I ran some tests locally, as well as dig through the CompletableFuture source-code and, at first glance, I do not see how any other type of exception could be thrown.)


回答1:


Is it possible for these methods to receive an exception other than CompletionException?

Yes, it is possible and you shouldn't cast to CompletionException without an instanceof check (or a review of your usage).

Take this example

CompletableFuture<Void> root = new CompletableFuture<>();
root.whenComplete((v, t) -> {
    System.out.println(t.getClass()); // class java.io.IOException
});
root.completeExceptionally(new IOException("blow it up"));

whenComplete will receive the IOException rather than a CompletionException wrapping it. The same behavior applies to exceptionally and handle.


A stage's computation is defined in the Javadoc:

The computation performed by a stage may be expressed as a Function, Consumer, or Runnable (using methods with names including apply, accept, or run, respectively) depending on whether it requires arguments and/or produces results.

I believe this quote

if a stage's computation terminates abruptly with an (unchecked) exception or error

is referring to one of those Function#apply, Consumer#accept, or Runnable#run methods terminating abruptly because of a thrown exception, not because a stage completed exceptionally through some other mechanism.

Note also that the Javadoc says

This interface does not define methods for initially creating, forcibly completing normally or exceptionally, probing completion status or results, or awaiting completion of a stage. Implementations of CompletionStage may provide means of achieving such effects, as appropriate

In other words, the interface allows implementations to complete stages exceptionally without abruptly terminating any computation. I think this allows for new behavior.


If we extend my example from before

CompletableFuture<Void> root = new CompletableFuture<>();
CompletableFuture<Void> child = root.whenComplete((v, t) -> {
    System.out.println(t.getClass()); // class java.io.Exception
});
child.whenComplete((v, t) -> {
    System.out.println(t.getClass()); // class java.util.concurrent.CompletionException
});
root.completeExceptionally(new IOException("blow it up"));

You'll notice the completion attached to the child receives a CompletionException wrapping the original IOException. This isn't obvious to me from the Javadoc, which states

Returns a new CompletionStage with the same result or exception as this stage

All in all, it seems like the raw exception from a completeExceptionally is passed down to direct dependents, while dependents of dependents receive an enclosing CompletionException.



来源:https://stackoverflow.com/questions/49230980/does-completionstage-always-wrap-exceptions-in-completionexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!