Just going through the CompletableFuture
documentation and stumbled upon the completeExceptionally
and obtrudeException
methods and is having a hard time comprehending the difference and use case. Can the community help understand the difference and the use case with an example?

- 896
- 3
- 17
- 34
-
3According to the documentation they're very similar, but completeExceptionally would not cause get to throw an exception if the task had already completed. obtrudeException seems to cause get to throw an exception regardless. – matt Sep 04 '21 at 11:44
2 Answers
Explanation
The difference is subtle but important. From the official documentation:
If not already completed, causes invocations of
get()
and related methods to throw the given exception.
Forcibly causes subsequent invocations of method
get()
and related methods to throw the given exception, whether or not already completed. [...]
So they differ in their behavior regarding CompletableFuture
s that are already completed.
Basically, a future can either be completed or still pending (not completed). When you call completeExceptionally
or obtrudeException
, the behavior differs depending on the state of the future at that point in time.
Already completed future
Consider this example where the future is already completed at the moment of calling the method:
CompletableFuture<String> future = CompletableFuture.completedFuture("hello world");
future.completeExceptionally(new RuntimeException("Oh noes!"));
System.out.println(future.get()); // Prints "hello world" just fine
versus
CompletableFuture<String> future = CompletableFuture.completedFuture("hello world");
future.obtrudeException(new RuntimeException("Oh noes!"));
System.out.println(future.get()); // Throws the exception
Not completed future
And in case the future is not completed yet, they will both throw an exception:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
return "hello world";
});
future.completeExceptionally(new RuntimeException("Oh noes!"));
System.out.println(future.get());
and
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
return "hello world";
});
future.obtrudeException(new RuntimeException("Oh noes!"));
System.out.println(future.get());
complete
and obtrudeValue
Likewise there are also the methods complete
and obtrudeValue
which behave in the same way, but instead of throwing an exception, you can supply a value instead.
So complete
basically completes the future with the given value, in case the future is not done yet, otherwise it does not do anything.
While obtrudeValue
will supply the given value regardless, so it resets or cancels whatever the future already computed and replaces it by the given value instead.

- 25,064
- 8
- 58
- 82
-
It sounds like I would always need the `complete` or `completeExceptionally` behavior. Could you add a case when we would actually want to use `obtrudeValue` or `obtrudeException` – RBz Sep 04 '21 at 12:32
-
1@RBz I have never encountered one myself but the documentation hints at *"error recovery"*. So I could imagine a case where, during your future computation, something goes wrong somewhere else and you want to basically cancel your future computations and replace the result with something entirely different. In that case, `obtrudeXXX` could be helpful since it is a *"regardless of what the future is currently doing, do this instead now"* – Zabuzard Sep 04 '21 at 12:34
-
2Maybe debugging. Consider a scenario where you want to verify your assumption that a future won’t be used after a certain point. Then, you could change it to a specific exception or marker value that would deliberately break any code that tries to use the future. But I also never used the `obtrude…` methods. – Holger Sep 06 '21 at 08:20
completeExceptionally:
completableFuture.completeExceptionally(
new RuntimeException("Calculation failed!"));
//..
completableFuture.get(); //exception will be thrown whether `completableFuture` was not already completed.
obtrudeException:
completableFuture.obtrudeException(
new RuntimeException("Calculation failed!"));
//..
completableFuture.get(); //exception will be thrown **whether or not** `completableFuture` was completed.

- 5,703
- 2
- 16
- 35