#
CompletableFuture class in Java
Future
interface is used to represent the future result of an asynchronous computation.
The interface provides the methods to check if the computation is completed or not,
to wait for its completion, and to retrieve the result of the computation.
CompletableFuture
class is an implementation of Future
and CompletionStage
interfaces.
This feature was introduced in Java 8.
CompletableFuture class is used generally when we need to run a task, and the result of that task be used as an input for the next task.
The usage is not very complicated. Take a look at the following example and read carefully the comment added in the code.
Firstly, we define a consumer, a supplier and a function.
package com.exampe.java;
import java.util.StringTokenizer;
import java.util.function.Consumer;
public class Consumer1 implements Consumer<String> {
public void accept(String strIn) {
StringTokenizer st = new StringTokenizer(strIn);
Integer nrWords = st.countTokens();
System.out.println("From Consumer1 / Words in string = "+nrWords);
}
}
package com.exampe.java;
import java.util.function.Supplier;
public class Supplier1 implements Supplier<String> {
@Override
public String get() {
String returnOfSupplier1 = "String from Suplier 1";
System.out.println("Return of Supplier 1: "+returnOfSupplier1);
return returnOfSupplier1;
}
}
package com.exampe.java;
import java.util.function.Function;
public class Function1 implements Function<String, String> {
@Override
public String apply(String strIn) {
String returnOfFunction1 = strIn.toUpperCase();
System.out.println("Return of Function1: "+returnOfFunction1);
return returnOfFunction1;
}
}
And here we have the main class:
package com.exampe.java;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.concurrent.*;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) throws InterruptedException, ExecutionException {
SpringApplication.run(DemoApplication.class, args);
System.out.println("--------------------------------------------");
Runnable runnable1 = () -> {
{
System.out.println("Runnable #1 is doing something");
};
};
Supplier1 supplier1 = new Supplier1();
Function1 f1 = new Function1();
Consumer1 c1 = new Consumer1();
CompletableFuture<String> cf1 = CompletableFuture
// runAsync runs a Runnable. There is no result.
.runAsync(runnable1)
// supplyAsync run ASYNC and return an object created by the Supplier supplier1
.supplyAsync(supplier1)
// The result from the code above is taken by "f1" and after that a result is returned
.thenApply(f1)
// The result of the code above is taken by a consumer and something is done with it.
.thenAccept(c1)
// Run a Runnable after the code above
.thenRun(runnable1)
.handle((result, exception) -> {
if (result != null) {
System.out.println("Oops! We have an exception - " + exception.getMessage());
return "Unknown!";
}
return "No result";
});
String result = cf1.get();
System.out.println("result="+result);
}
}
Info
"get()" method blocks the current thread and returns the value from a ASYNC call (if any ASYNC call as supplyAsync()).
When you run the code you will see:
--------------------------------------------
Runnable #1 is doing something
Return of Supplier 1: String from Suplier 1
Return of Function1: STRING FROM SUPLIER 1
From Consumer1 / Words in string = 4
Runnable #1 is doing something
result=No result
Process finished with exit code 0
When we handle the error, we can use exceptionally()
, completeExceptionally()
or whenCompleted()
methods.
Info
"then" from the method name signifies that the action will be done sequentially.
You can also take a look at the following articles: