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 value
  • supplyAsync() → 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

  1. Get Customer
  2. Get Customer Orders
  3. 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() with thenCompose().
  • Using Future for complex asynchronous workflows.
  • Ignoring exception handling.
  • Creating unnecessary nested CompletableFuture objects.
  • Forgetting to use a custom ExecutorService for CPU-intensive tasks.
  • Blocking threads unnecessarily with join() or get().

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() and supplyAsync().
  • Understand asynchronous execution.

Senior Java Developer

  • Build asynchronous pipelines.
  • Use thenCompose() and thenCombine().
  • 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.