Can you manually complete a CompletableFuture with a result
You can also complete with an exception:
🟢 Junior Level
Yes! The complete() method allows you to manually complete a CompletableFuture:
CompletableFuture<String> cf = new CompletableFuture<>();
// Manually complete with a result
cf.complete("Hello");
// Now cf is completed
System.out.println(cf.isDone()); // true
System.out.println(cf.join()); // Hello
You can also complete with an exception:
cf.completeExceptionally(new RuntimeException("Error"));
🟡 Middle Level
complete()
CompletableFuture<String> cf = new CompletableFuture<>();
// Complete
boolean completed = cf.complete("result"); // true
// Repeated complete is ignored
completed = cf.complete("other"); // false
System.out.println(cf.join()); // "result" — first one wins
completeExceptionally()
cf.completeExceptionally(new RuntimeException("Error"));
// When trying to get the result
try {
cf.join();
} catch (CompletionException e) {
System.out.println(e.getCause()); // RuntimeException: Error
}
obtrudeValue() — forced completion
cf.complete("first");
cf.obtrudeValue("overridden"); // forcibly changes
System.out.println(cf.join()); // "overridden"
Typical mistakes
- Using obtrudeValue without a good reason:
// ⚠️ obtrudeValue violates the "single completion" contract of CompletableFuture. // Any code that already received the result and started processing may work // with stale data. Only those who access the CF AFTER the obtrudeValue call // will see the new result. In a multi-threaded environment — race conditions. cf.obtrudeValue("new value");
🔴 Senior Level
Internal Implementation
public boolean complete(T value) {
boolean triggered = completeValue(value);
if (triggered)
postComplete(); // execute all callbacks
return triggered;
}
public void obtrudeValue(T value) {
result = (value == null) ? NIL : value; // force set
}
Production Experience
Manual completion in tests:
@Test
void testManualCompletion() {
CompletableFuture<String> cf = new CompletableFuture<>();
// Simulate async completion
new Thread(() -> {
Thread.sleep(100);
cf.complete("done");
}).start();
assertEquals("done", cf.get(1, TimeUnit.SECONDS));
}
Best Practices
// ✅ complete() for manual completion
cf.complete(result);
// ✅ completeExceptionally() for errors
cf.completeExceptionally(error);
// ❌ obtrudeValue() without a good reason
// ❌ Ignoring the return value of complete()
🎯 Interview Cheat Sheet
Must know:
- complete(value) — completes CF if not already completed. Returns true/false
- completeExceptionally(ex) — completes CF with an error
- obtrudeValue(value) — forcibly changes the result, violating the single-completion contract
- First complete wins, subsequent ones are ignored
- On completion, all pending callbacks are executed (postComplete)
Common follow-up questions:
- complete() called twice — what happens? — First returns true, second false. Result = first value
- obtrudeValue vs complete? — obtrudeValue force-sets even if CF is already completed. Dangerous
- completeExceptionally + complete — what happens? — Depends on timing. Whoever is first wins
- Why manual completion? — Tests, cache, conditional async, integration with blocking API
Red flags (DO NOT say):
- “complete() can be called multiple times” — only the first succeeds
- “obtrudeValue is safe for production” — race conditions, violates single-completion contract
- “completeExceptionally doesn’t execute callbacks” — it does, just like complete
Related topics:
- [[3. How to create a CompletableFuture that is already completed with a result]]
- [[20. Can you reuse a single CompletableFuture in multiple chains]]
- [[18. How to cancel CompletableFuture execution]]
- [[24. How to test code with CompletableFuture]]