Java Language – 77 – CompletableFuture

Java 8 Features – CompletableFuture
Introduction to CompletableFuture

Java 8 introduced the CompletableFuture class as part of the Concurrency API to simplify asynchronous programming and handle tasks that may take a considerable amount of time to complete. With CompletableFuture, developers can write non-blocking, asynchronous code that can perform tasks concurrently, improving application responsiveness.

Creation of CompletableFuture

Creating a CompletableFuture is straightforward. You can either create one from scratch or apply asynchronous operations to an existing CompletableFuture.

Here’s an example of creating a simple CompletableFuture that completes with a specified value:


import java.util.concurrent.CompletableFuture;

public class CompletableFutureCreationExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.completedFuture("Hello, CompletableFuture!");

        future.thenAccept(result -> System.out.println(result));
    }
}
Chaining Operations with thenApply

One of the powerful features of CompletableFuture is the ability to chain multiple operations asynchronously. You can use the thenApply method to apply a function to the result when it’s completed. This is especially useful for transforming data or applying additional processing.

Here’s an example of chaining thenApply operations:


import java.util.concurrent.CompletableFuture;

public class CompletableFutureChainingExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.completedFuture("Hello, CompletableFuture!");

        CompletableFuture<Integer> lengthFuture = future.thenApplyAsync(s -> s.length());

        lengthFuture.thenAccept(result -> System.out.println("Length: " + result));
    }
}
Handling Exceptions with exceptionally

When working with asynchronous tasks, it’s essential to handle exceptions gracefully. You can use the exceptionally method to handle exceptions and return a default value or perform recovery operations.

Here’s an example of using exceptionally:


import java.util.concurrent.CompletableFuture;

public class CompletableFutureExceptionHandlingExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            if (Math.random() < 0.5) {
                throw new RuntimeException("Failed!");
            }
            return "Success";
        });

        future.exceptionally(ex -> "Default Value")
                .thenAccept(result -> System.out.println("Result: " + result));
    }
}
Combining CompletableFutures

You can also combine multiple CompletableFutures to create more complex asynchronous workflows. The thenCombine and thenCompose methods allow you to combine results or chain multiple futures together.

Here’s an example of using thenCombine to combine two CompletableFutures:


import java.util.concurrent.CompletableFuture;

public class CompletableFutureCombiningExample {
    public static void main(String[] args) {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 2);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);

        CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (result1, result2) -> result1 * result2);

        combinedFuture.thenAccept(result -> System.out.println("Result: " + result));
    }
}
Completing Future Asynchronously

While most operations in CompletableFuture are asynchronous by default, you can also complete a CompletableFuture asynchronously using the completeAsync method.

Here’s an example of completing a CompletableFuture asynchronously:


import java.util.concurrent.CompletableFuture;

public class CompletableFutureCompleteAsyncExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = new CompletableFuture<>();

        CompletableFuture.runAsync(() -> future.complete("Async Completion"));

        future.thenAccept(result -> System.out.println(result));
    }
}
Conclusion

CompletableFuture is a valuable addition to Java’s concurrency framework, making it easier to write non-blocking, asynchronous code. With its intuitive methods for chaining operations, handling exceptions, and combining multiple futures, developers can create responsive and efficient applications.