Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Completable future - complete method

I have a code:

CompletableFuture<Integer> c1 = new CompletableFuture<Integer>()
        .thenApply((data) -> data * 2);
c1.thenAccept(System.out::println);

c1.complete(20);


CompletableFuture<Integer> c2 = new CompletableFuture<>();

c2.thenApply(data -> data * 2)
        .thenAccept(System.out::println);
c2.complete(20);

Output:

20 40

Question:

  1. Why there is a difference in output between c1 and c2?
  2. Why there is a need to repeat future type in c1 by calling:

new CompletableFuture<Integer>()

like image 627
Thomas Banderas Avatar asked Mar 01 '19 00:03

Thomas Banderas


People also ask

What is completed stage method of CompletableFuture interface?

Returns a new CompletableFuture that is asynchronously completed by a task running in the given executor with the value obtained by calling the given Supplier. Returns a new CompletionStage that, when this stage completes normally, is executed with this stage's result as the argument to the supplied action.

What are the benefits of Completable Future?

CompletableFuture implements Future and CompletionStage interfaces and provides a huge set of convenience methods for creating, chaining and combining multiple Futures. It also has a very comprehensive exception handling support.

Is Completable Future multithreaded?

The CompletableFuture, was introduced in Java 8, provides an easy way to write asynchronous, non-blocking and multi-threaded code.


1 Answers

The first thing to note is that the methods of CompletableFuture (e.g. thenApply, thenAccept, etc.) return a new CompletableFuture instance. This forms a sort of "chain" where each new stage is dependent on the stage it was created from—its parent stage. When a stage completes, normally or exceptionally, the result is pushed to all its dependent, non-completed* stages (the same stage can have multiple dependent stages).


* As you'll see below you can complete a stage even if its parent hasn't completed yet. If and when the parent stage completes the completed dependent stage(s) will not be invoked as it's already completed. The consequences of this are covered briefly in an answer by Holger to another question.


Question 1

In your first example you have the following:

CompletableFuture<Integer> c1 = new CompletableFuture<Integer>()
        .thenApply((data) -> data * 2);
c1.thenAccept(System.out::println);
c1.complete(20);

Here c1 is the stage resulting from thenApply, not new CompletableFuture<Integer>(). When you call c1.complete(20) you are completing the thenApply stage (normally) with the given value (20). The call to complete is equivalent to the Function transforming the previous stage's result and returning 20. Now that thenApply is completed it pushes the value to thenAccept which results in 20 being printed to the console.

In your second example you have the following:

CompletableFuture<Integer> c2 = new CompletableFuture<>();
c2.thenApply(data -> data * 2)
        .thenAccept(System.out::println);
c2.complete(20);

Here c2 is the stage resulting from new CompletableFuture<>(), which is the parent of the thenApply stage. So now when you call c2.complete(20) you are completing the root stage which pushes the value to thenApply. The Function then transforms the value by multiplying it by 2 and pushing that result to thenAccept. This results in 40 being printed out to the console.


Question 2

The reason you must repeat <Integer> in your first example is because the compiler cannot infer the type of the first stage without it. The signature of thenApply is:

<U> CompletableFuture<U> thenApply(Function<? super T, ? extends U>)

The T is determined by the type of this CompletableFuture (the one the method is invoked on). The U is determined by the Function and, to an extent, the left hand side of a variable assignment where applicable. This means when you use the diamond operator (<>) you are effectively using the following:

CompletableFuture<Integer> c = new CompletableFuture<Object>()
        .thenApply(data -> data * 2);

// same as...
CompletableFuture<Integer> c = new CompletableFuture<>()
        .thenApply(data -> data * 2);

Since all the compiler knows about the type of data is that it's an Object the multiplication is invalid; an Object cannot be multiplied by 2. Note that the above would be valid if you simply changed the Function from data -> data * 2 to data -> 2 (but obviously those two functions aren't equivalent). This is because the left hand side of the assignment is related to the result of thenApply, not new CompletableFuture<>().

When you explicitly specify <Integer> the compiler then knows that the input type (T) of the thenApply stage is Integer, which means it knows data is an Integer; an Integer can be multiplied by 2.

like image 50
Slaw Avatar answered Oct 04 '22 05:10

Slaw