Question 27 · Section 19

Can you manually complete a CompletableFuture with a result

You can also complete with an exception:

Language versions: English Russian Ukrainian

🟢 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

  1. 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]]