Undocumented behaviour in CompletableFuture
Sebastian Stenzel
sebastian.stenzel at gmail.com
Mon Aug 24 14:47:15 UTC 2020
Hi all,
I just stumbled upon a strange behaviour and I'm not sure if it is intended this way or whether it's a bug. It is also possible I'm using the API incorrectly. Let me explain with an example:
```
var originalException = new Exception("foo");
CompletableFuture.failedFuture(originalException).exceptionally(e -> {
assert originalException == e;
return null;
});
```
This works as expected. However the following does not:
```
var originalException = new Exception("foo");
CompletableFuture.failedFuture(originalException).thenCompose(obj -> CompletableFuture.completedFuture(obj)).exceptionally(e -> {
assert originalException == e; // FAIL!
return null;
});
```
Note, that the "identity"-function passed to `thenCompose(...)` will not even be called, since the future is already in failed state. However CompletableFuture's implementation wraps the original exception into a new CompletionException. So the mere presence of a `thenCompose(...)` somewhere in the chain will change how to deal with exceptions further downstream.
Neither do I find this side effect `thenCompose` documented, nor is it part of the contract of `exceptionally(...)` or `handle(...)` to only return CompletionExceptions. And don't get me started on `get()` and `join()`...
Depending on the context of your code, you don't know whether or not there was a `thenCompose`, so you need to check for both, the expected exception as well as a CompletionException with its cause being the expected exception.
So is this a bug? Or is this a thing that is not going to be changed, since other software already implemented workarounds for this behaviour? If so, should I move to 3rd party libs?
Cheers!
Sebastian
More information about the core-libs-dev
mailing list