Future and CompletableFuture - Interview Questions & Answers
Master Java Future and CompletableFuture with interview-focused questions and answers. Learn asynchronous programming, callbacks, chaining, parallel execution, and production-ready Java examples.
Future and CompletableFuture - Interview Questions & Answers
Introduction
Modern enterprise applications rarely call a single service.
For example, when a user opens an e-commerce product page, the backend may simultaneously call:
- Product Service
- Pricing Service
- Inventory Service
- Recommendation Service
- Review Service
If these services are called one after another, the response becomes slow.
Java provides Future and CompletableFuture to execute tasks asynchronously and improve response times.
Why Interviewers Ask About Future and CompletableFuture?
These APIs are widely used in:
- Spring Boot
- Microservices
- REST APIs
- Kafka Consumers
- Payment Systems
- Banking Applications
Interviewers expect developers to understand:
- Asynchronous Programming
- Non-blocking Execution
- Callback Processing
- Parallel API Calls
- CompletableFuture Pipelines
flowchart TD
Client --> Service
Service --> ProductAPI
Service --> PricingAPI
Service --> InventoryAPI
Service --> ReviewAPI
ProductAPI --> Response
PricingAPI --> Response
InventoryAPI --> Response
ReviewAPI --> Response
Interview Question 1
What is Future?
Answer
A Future represents the result of an asynchronous computation.
Instead of waiting for a task to finish immediately, a Future allows the application to continue executing while the task runs in the background.
The result can be retrieved later.
Diagram
flowchart LR
Task --> ExecutorService --> Future --> Result
Java Example
ExecutorService executor =
Executors.newSingleThreadExecutor();
Future<String> future =
executor.submit(() -> {
Thread.sleep(2000);
return "Completed";
});
System.out.println("Doing other work...");
System.out.println(future.get());
executor.shutdown();
Output
Doing other work...
Completed
Production Example
An online banking system submits a report generation task and allows the user to continue browsing while the report is being prepared.
Interview Tip
Future executes tasks asynchronously, but calling get() blocks until the result is available.
Interview Question 2
What are the limitations of Future?
Answer
Although Future introduced asynchronous programming, it has several limitations.
Limitations
- Blocking
get() - No callback support
- Cannot combine multiple Futures
- Difficult error handling
- No task chaining
Diagram
flowchart TD
Future --> get() --> ThreadBlocked
ThreadBlocked --> Result
Java Example
Future<String> future =
executor.submit(() -> "Java");
String result = future.get();
The current thread waits until the task completes.
Interview Tip
Future is useful for simple asynchronous tasks, but it becomes difficult to manage complex asynchronous workflows.
Interview Question 3
What is CompletableFuture?
Answer
CompletableFuture is an enhanced version of Future introduced in Java 8.
It supports:
- Non-blocking programming
- Callback methods
- Task chaining
- Combining multiple tasks
- Exception handling
- Parallel execution
Diagram
flowchart LR
Task --> CompletableFuture --> thenApply --> thenCompose --> thenAccept
Java Example
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() ->
"Hello Java"
);
future.thenAccept(System.out::println);
Output
Hello Java
Production Example
A Spring Boot application calls multiple downstream services in parallel and combines all responses before returning the final result.
Interview Tip
CompletableFuture enables asynchronous programming without explicitly managing threads.
Interview Question 4
What is the difference between Future and CompletableFuture?
Answer
CompletableFuture extends the capabilities of Future.
Comparison
| Future | CompletableFuture |
|---|---|
| Blocking get() | Non-blocking callbacks |
| No chaining | Supports chaining |
| No callbacks | Callback support |
| No combination | Combine multiple tasks |
| Basic exception handling | Advanced exception handling |
| Introduced in Java 5 | Introduced in Java 8 |
Diagram
flowchart LR
Future --> Blocking
CompletableFuture --> AsyncPipeline
Java Example
Future
Future<String> future =
executor.submit(() -> "Java");
CompletableFuture
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() ->
"Java"
);
Interview Tip
Whenever possible, prefer CompletableFuture over Future in modern applications.
Interview Question 5
How do you create a CompletableFuture?
Answer
There are multiple ways to create a CompletableFuture.
Method 1
Run without returning a result.
CompletableFuture.runAsync(() -> {
System.out.println("Running");
});
Method 2
Run and return a value.
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() ->
"Spring Boot"
);
Diagram
flowchart TD
CompletableFuture --> runAsync
CompletableFuture --> supplyAsync
runAsync --> NoResult
supplyAsync --> ReturnValue
Production Example
A product search service executes database queries asynchronously using supplyAsync() and returns the results after completion.
Interview Tip
Remember:
runAsync()→ No return valuesupplyAsync()→ Returns a result
Interview Question 6
What is thenApply()?
Answer
thenApply() is used to transform the result of a completed CompletableFuture.
It accepts the output of one stage and returns a modified value for the next stage.
Diagram
flowchart LR
Task --> Result --> thenApply --> ModifiedResult
Java Example
CompletableFuture<String> future =
CompletableFuture
.supplyAsync(() -> "java")
.thenApply(String::toUpperCase);
System.out.println(future.join());
Output
JAVA
Production Example
Receive a customer name from the database and convert it into a formatted response before returning it to the client.
Interview Tip
Use thenApply() when you want to modify the previous result.
Interview Question 7
What is thenCompose()?
Answer
thenCompose() is used when one asynchronous operation depends on another asynchronous operation.
It avoids nested CompletableFuture objects.
Diagram
flowchart LR
UserService --> User --> thenCompose --> OrderService --> Orders
Java Example
CompletableFuture<String> future =
CompletableFuture
.supplyAsync(() -> "Customer")
.thenCompose(customer ->
CompletableFuture.supplyAsync(
() -> customer + " Orders")
);
System.out.println(future.join());
Output
Customer Orders
Production Example
- Get Customer
- Get Customer Orders
- Get Order Details
Each call depends on the previous result.
Interview Tip
Remember:
- thenApply() → Transform value
- thenCompose() → Chain asynchronous tasks
Interview Question 8
What is thenCombine()?
Answer
thenCombine() combines the results of two independent asynchronous tasks.
Both tasks execute in parallel.
Diagram
flowchart TD
ProductAPI --> thenCombine
PricingAPI --> thenCombine
thenCombine --> FinalResponse
Java Example
CompletableFuture<String> product =
CompletableFuture.supplyAsync(
() -> "Laptop");
CompletableFuture<Integer> price =
CompletableFuture.supplyAsync(
() -> 50000);
CompletableFuture<String> result =
product.thenCombine(price,
(p, amount) ->
p + " : " + amount
);
System.out.println(result.join());
Output
Laptop : 50000
Production Example
An e-commerce application retrieves:
- Product Details
- Product Price
simultaneously and combines them into a single response.
Interview Tip
Use thenCombine() only when the two tasks are independent.
Interview Question 9
What are allOf() and anyOf()?
Answer
These methods coordinate multiple asynchronous tasks.
allOf()
Waits until all tasks complete.
anyOf()
Returns when any one task completes.
Diagram
flowchart TD
Task1 --> allOf
Task2 --> allOf
Task3 --> allOf
allOf --> Continue
Java Example
CompletableFuture<Void> future =
CompletableFuture.allOf(
CompletableFuture.runAsync(() ->
System.out.println("Task 1")),
CompletableFuture.runAsync(() ->
System.out.println("Task 2"))
);
future.join();
Production Example
A banking dashboard loads:
- Account Details
- Recent Transactions
- Reward Points
- Loan Information
All services execute simultaneously.
Interview Tip
- allOf() → Wait for every task.
- anyOf() → Use the fastest completed task.
Interview Question 10
How do you handle exceptions in CompletableFuture?
Answer
CompletableFuture provides several methods for exception handling.
Common methods:
- exceptionally()
- handle()
- whenComplete()
Diagram
flowchart LR
Task --> Exception
Exception --> exceptionally
exceptionally --> DefaultValue
Java Example
CompletableFuture<Integer> future =
CompletableFuture
.supplyAsync(() -> 10 / 0)
.exceptionally(ex -> {
System.out.println(ex.getMessage());
return 0;
});
System.out.println(future.join());
Output
0
Production Example
If an external payment API fails, return a fallback response instead of crashing the application.
Interview Tip
Always implement exception handling for asynchronous pipelines to improve application resilience.
Common Interview Mistakes
- Calling
get()immediately after submitting a task. - Confusing
thenApply()withthenCompose(). - Using
Futurefor complex asynchronous workflows. - Ignoring exception handling.
- Creating unnecessary nested
CompletableFutureobjects. - Forgetting to use a custom ExecutorService for CPU-intensive tasks.
- Blocking threads unnecessarily with
join()orget().
Quick Revision Cheat Sheet
| Concept | Key Point |
|---|---|
| Future | Represents an asynchronous result |
| CompletableFuture | Advanced asynchronous programming API |
| runAsync() | Executes task without returning a value |
| supplyAsync() | Executes task and returns a result |
| thenApply() | Transforms a result |
| thenCompose() | Chains dependent asynchronous tasks |
| thenCombine() | Combines independent asynchronous tasks |
| allOf() | Waits for all tasks |
| anyOf() | Waits for the first completed task |
| exceptionally() | Handles exceptions |
Interviewer's Expectations
Junior Java Developer
- Understand Future.
- Explain CompletableFuture basics.
- Use
runAsync()andsupplyAsync(). - Understand asynchronous execution.
Senior Java Developer
- Build asynchronous pipelines.
- Use
thenCompose()andthenCombine(). - Handle exceptions correctly.
- Execute parallel service calls efficiently.
- Avoid blocking operations.
Solution Architect
- Design scalable asynchronous systems.
- Parallelize microservice communication.
- Improve response time using CompletableFuture.
- Handle failures gracefully.
- Integrate CompletableFuture with Spring Boot and reactive architectures where appropriate.
Related Interview Questions
- ExecutorService
- ForkJoinPool
- Java Concurrency Basics
- Locks
- Atomic Classes
- Concurrent Collections
- Parallel Streams
- Java Memory Model
- Virtual Threads (Java 21)
- Reactive Programming
Summary
Future introduced asynchronous execution in Java, but it has limitations such as blocking result retrieval and limited composition capabilities. CompletableFuture addresses these limitations by providing a rich API for non-blocking execution, task chaining, combining independent operations, exception handling, and parallel processing.
In modern enterprise applications, CompletableFuture is widely used to improve performance by executing multiple service calls concurrently and composing their results into a single response. For interviews, focus not only on the API methods but also on when to use thenApply(), thenCompose(), thenCombine(), allOf(), and proper exception handling, supported by real production examples such as microservice orchestration, payment processing, and dashboard aggregation.