The CompletionStage Javadoc states:
[...] if a stage\'s computation terminates abruptly with an (unchecked) exception or error, then all dependent sta
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
, orRunnable
(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
.