How do you manually drive a CompletableFuture to completion, and what is the difference between complete(value) and completeExceptionally(throwable)?
answer
- complete = success, completeExceptionally = failure
- returns true only if YOU settled it (one winner)
- new CompletableFuture<>() starts incomplete
- bridges callback APIs into the CF world
- failure shows downstream as CompletionException(cause)
basics
~20 sA CompletableFuture isn't always finished automatically — you can finish it yourself. complete(value) makes it succeed with that value; completeExceptionally(ex) makes it fail with that exception. Both return true only if your call was the one that completed it.
solid answer
~40 sA CompletableFuture is a holder for a result that may arrive later. When you create one with `new CompletableFuture<>()` it starts incomplete, and code can manually settle it. `complete(value)` settles it successfully so downstream stages run with that value; `completeExceptionally(throwable)` settles it as a failure so downstream stages see that exception (wrapped in a CompletionException by join/then-stages). Each returns a boolean: true if this call transitioned the future from incomplete to complete, false if it was already settled — so only the first winner takes effect, later calls are ignored silently. This is the bridge pattern for adapting callback APIs: hand the future to a caller, then call complete/completeExceptionally from your callback when the async work finishes.
go deeper
Knows complete sets a success value and completeExceptionally sets a failure, and that you create an incomplete future with the constructor.
Explains the boolean return (first caller wins, later calls no-op) and uses the pattern to adapt a callback API into a CompletableFuture.
Articulates exception wrapping (CompletionException/ExecutionException), how failure propagates past mapping stages, and the race-resolution semantics for competing completers.
Designs robust adapter boundaries around this: idempotent completion under concurrent settlers, leak-free futures that always get settled (success, error, or timeout path), and reasons about it as a single-assignment cell.
### What a CompletableFuture is A `CompletableFuture<T>` is a container that will eventually hold either a **value** of type `T` or a **failure** (a `Throwable`). At any moment it is in one of two states: **incomplete** (no result yet) or **completed** (settled, either successfully or exceptionally). "Settled" is permanent — once it holds a result, that result never changes through normal means. Unlike a plain `Future`, a `CompletableFuture` can be **completed from the outside** — that's the "Completable" in the name. You don't have to wait for some executor to fill it; your own code can push a result into it. ### Creating an incomplete one ```java CompletableFuture<String> cf = new CompletableFuture<>(); ``` Right now `cf` has no value. Anyone who calls `cf.join()` will block forever until something settles it. ### complete(value) `boolean complete(T value)` tries to settle the future **successfully** with `value`. - If the future was incomplete, it becomes completed-successfully, every dependent stage (`thenApply`, `thenAccept`, …) that was waiting fires, and the method returns `true`. - If the future was **already** completed (by anyone, any way), nothing changes and it returns `false`. ### completeExceptionally(throwable) `boolean completeExceptionally(Throwable ex)` tries to settle the future as a **failure**. - If incomplete: it becomes completed-exceptionally with `ex`. Dependent stages that handle errors (`exceptionally`, `handle`, `whenComplete`) see it; plain mapping stages (`thenApply`) are skipped and the failure propagates. Returns `true`. - If already completed: no change, returns `false`. ### Why the boolean return matters Completion is a **race that exactly one caller wins**. Imagine two threads — one calls `complete("ok")`, another calls `completeExceptionally(new IOException())`. Exactly one of them returns `true` (the winner that flipped the state); the other returns `false` and is ignored. This makes the future a safe one-shot signal: you can have a success path and a timeout/cancel path racing, and whichever fires first wins deterministically. ### How failures look downstream When you read a failed future, the exception is usually **wrapped**. `join()` and the `thenX` chain wrap your `Throwable` in a `CompletionException` (and `get()` wraps it in an `ExecutionException`). So if you `completeExceptionally(new IllegalStateException("boom"))`, a caller doing `cf.join()` catches a `CompletionException` whose `getCause()` is your `IllegalStateException`. ### The classic use case: adapting a callback API ```java CompletableFuture<Response> call(Request r) { CompletableFuture<Response> cf = new CompletableFuture<>(); httpClient.sendAsync(r, new Callback() { public void onSuccess(Response resp) { cf.complete(resp); } public void onError(Throwable t) { cf.completeExceptionally(t); } }); return cf; // returned still-incomplete; settles when the callback fires } ``` The caller gets a future immediately and composes on it; your callback later pushes the result in. This is the canonical way to turn any asynchronous callback-style API into the composable `CompletableFuture` world.
- If two threads call complete() with different values on the same future, what value wins?Whichever call ran first and returned true; the other returns false and its value is discarded. Completion is a one-shot, first-wins transition.
- How does a downstream thenApply stage behave when the future is completed exceptionally?thenApply is skipped entirely; the failure propagates down the chain until a handling stage (exceptionally/handle/whenComplete) is reached.
saying these in an interview costs you the question
- Thinking complete() always succeeds — it returns false if already settled
- Believing a second complete() overwrites the first value
- Forgetting that join sees a CompletionException wrapper, not your raw exception
- Assuming completeExceptionally throws — it only sets the state, never throws