Card 06/ 09

ComparisonDifficulty: Advanced1 min

Future Against CompletableFuture

A Future gives you one thing: a way to wait for a result. That is enough until you want to do something with the result without a thread standing there waiting for it.

What each one lets you do with a pending result
Compared onFutureCompletableFuture
Get the resultget(), which blocksjoin(), or a callback that does not
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 is a description that 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. Reach for the composable one when a result feeds into another call, when two results have to be combined, or when there is no thread you can afford to block.