Medium java java-21

What is the difference between Future and CompletableFuture?

The 30 second answer

A Future only lets you check if the work is done, cancel it, or block on get to wait for the result. CompletableFuture, added in Java 8, lets me chain the next steps without blocking: transform the result, call another async step, combine two results, or wait for many. It also handles errors in the chain. For blocking I/O, I pass my own executor instead of using the common pool.

Future<T> (Java 5) offers isDone(), cancel() and a blocking get(). You can't attach a callback or compose two futures.

CompletableFuture<T> (Java 8) implements both Future and CompletionStage:

  • thenApply transforms the result; thenCompose chains another async call (like flatMap).
  • thenCombine merges two independent results; allOf / anyOf wait for several.
  • exceptionally, handle and whenComplete deal with failures.
  • complete(value) lets you finish it by hand.
CompletableFuture<List<Order>> orders =
    CompletableFuture.supplyAsync(() -> loadUser(id), ioPool)
        .thenCompose(u -> CompletableFuture.supplyAsync(() -> loadOrders(u), ioPool))
        .orTimeout(2, TimeUnit.SECONDS)          // Java 9
        .exceptionally(ex -> List.of());

Async methods without an executor argument use ForkJoinPool.commonPool(). [Opinion] For blocking I/O pass a dedicated executor so you don't starve the common pool, which parallel streams also share. On Java 21, a virtual-thread-per-task executor is a good fit.

Sources: Future Javadoc, CompletableFuture Javadoc

Sources (0)
See inline citations in the answer above.