What is a Future, and how does CompletableFuture improve on it?
Quick Answer
Future<V> represents the result of an asynchronous computation; you can check isDone(), cancel it, or call the blocking get() to retrieve the result once ready, but plain Future offers no way to chain follow-up actions, combine multiple futures, or register a non-blocking callback. CompletableFuture (Java 8+) implements Future but adds a rich, composable API — thenApply, thenCompose, thenCombine, exceptionally, allOf — for building non-blocking asynchronous pipelines.
Detailed Answer
Future<V> (Java 5+) represents a value that will be available later, from an asynchronous computation submitted to an ExecutorService:
Future<Integer> future = executor.submit(() -> compute());
future.isDone(); // check without blocking
int result = future.get(); // blocks until the result is ready (or throws)
future.cancel(true); // attempt to cancel
Its big limitation: there's no way to react to completion without blocking — no callback registration, no chaining a follow-up computation, no combining two futures' results. You either poll isDone() in a loop or block on get().
CompletableFuture<T> (Java 8+) implements Future but adds a rich, functional, non-blocking composition API:
CompletableFuture<Integer> cf = CompletableFuture
.supplyAsync(() -> fetchUserId())
.thenApply(id -> id * 2) // transform the result
.thenCompose(id -> fetchProfileAsync(id)) // chain another async call
.exceptionally(ex -> { log(ex); return null; }); // handle failure
CompletableFuture<Void> both = CompletableFuture.allOf(cf1, cf2, cf3); // wait for several
CompletableFuture<Integer> combined = cf1.thenCombine(cf2, (a, b) -> a + b);
Key additions over plain Future:
thenApply/thenAccept/thenRun: chain transformations without blocking.thenCompose: flatten a future-returning function (avoidsFuture<Future<T>>).thenCombine/allOf/anyOf: combine results from multiple independent futures.exceptionally/handle: structured error handling in the pipeline itself, instead of a try/catch around a blockingget().- Manual completion:
complete(value)lets non-executor code (e.g., a callback-based API) resolve the future directly.
CompletableFuture is the standard building block for non-blocking async pipelines in modern Java, similar in spirit to Promises in JavaScript.