Первое, что нужно отметить, - это то, что методы CompletableFuture
(например, thenApply
, thenAccept
и т. Д.) Возвращают новый CompletableFuture
экземпляр.Это образует своего рода «цепочку», где каждая новая стадия зависит от стадии, из которой она была создана - ее родительской стадии.Когда этап завершается, обычно или в исключительных случаях, результат передается на все его зависимые незавершенные * этапы (один и тот же этап может иметь несколько зависимых этапов).
* Как вы увидите ниже, вы можете завершить этап, даже если его родитель еще не завершен.Если и когда родительский этап завершится, зависимые этапы пройден не будут вызваны, так как он уже завершен.Последствия этого кратко описаны в ответе Хольгера на другой вопрос.
Вопрос 1
В первом примере у вас есть следующее:
CompletableFuture<Integer> c1 = new CompletableFuture<Integer>()
.thenApply((data) -> data * 2);
c1.thenAccept(System.out::println);
c1.complete(20);
Здесь c1
- это этап, полученный из thenApply
, а не new CompletableFuture<Integer>()
.Когда вы звоните c1.complete(20)
, вы завершаете этап thenApply
(обычно) с заданным значением (20
).Вызов complete
эквивалентен Function
, преобразующему результат предыдущего этапа и возвращающему 20
.Теперь, когда thenApply
завершено, значение увеличивается до thenAccept
, в результате чего на консоль выводится 20
.
Во втором примере у вас есть следующее:
CompletableFuture<Integer> c2 = new CompletableFuture<>();
c2.thenApply(data -> data * 2)
.thenAccept(System.out::println);
c2.complete(20);
Здесь c2
- этап, полученный из new CompletableFuture<>()
, который является родителем этапа thenApply
.Итак, теперь, когда вы вызываете c2.complete(20)
, вы завершаете этап root , который переводит значение в thenApply
.Затем Function
преобразует значение, умножив его на 2
и увеличив результат до thenAccept
.Это приводит к выводу 40
на консоль.
Вопрос 2
Причина, по которой вы должны повторить <Integer>
в первом примере, заключается в том, что компилятор не может определить типпервого этапа без него.thenApply
подпись:
<U> CompletableFuture<U> thenApply(Function<? super T, ? extends U>)
* T
определяется типом этого CompletableFuture
(тот, на котором вызывается метод).U
определяется Function
и, в некоторой степени, левой частью присвоения переменной, где это применимо.Это означает, что когда вы используете оператор diamond (<>
), вы эффективно используете следующее:
CompletableFuture<Integer> c = new CompletableFuture<Object>()
.thenApply(data -> data * 2);
// same as...
CompletableFuture<Integer> c = new CompletableFuture<>()
.thenApply(data -> data * 2);
Поскольку компилятор знает о типе data
, это умножение на Object
является недействительным;Object
нельзя умножить на 2
.Обратите внимание, что вышеприведенное будет справедливо, если вы просто измените Function
с data -> data * 2
на data -> 2
(но, очевидно, эти две функции не эквивалентны).Это связано с тем, что левая часть присваивания связана с результатом thenApply
, а не new CompletableFuture<>()
.
Когда вы явно указываете <Integer>
, компилятор знает, что тип ввода (T
) thenApply
ступени - Integer
, что означает, что data
- Integer
;Integer
можно умножить на 2
.