Card 06/ 09

ComparisonDifficulty: Advanced1 min

Future vs CompletableFuture

Waiting for a result, or describing what happens when it arrives.

A Future gives you one thing: a way to wait for a result. A CompletableFuture is a Future you can also attach steps to, saying what should happen when the result arrives, without a thread standing there waiting for it. The table compares what each lets you do with a pending result.

What each one lets you do with a pending result
Compared onFutureCompletableFuture
Get the resultget(), which blocksjoin(), or a callback that does not block
Do something when it arrivesNothing: you must waitthenApply, thenAccept, thenCompose
Combine two resultsWait for both in turnthenCombine
Handle a failureCatch around get()exceptionally, handle
Complete it yourselfNocomplete(value)
java
CompletableFuture<Long> rows = CompletableFuture.supplyAsync(() -> countRowsIn(file), pool);

rows.thenApply(n -> n * 2)
    .thenAccept(n -> log.info("{} rows", n))
    .exceptionally(e -> { log.error("failed", e); return null; });

Nothing blocks. Each stage says what to do when the previous one finishes, and the whole chain runs on the pool as results arrive: the same shape as a stream pipeline, over time instead of over elements.

The decision rule. A Future is enough when you submit a handful of tasks and wait for all of them. Use CompletableFuture when one result feeds another call, when two results have to be combined, or when there is no thread you can afford to block.