可完成的未来 - 完整的方法
Completable future - complete method
我有一个代码:
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);
输出:
20 40
问题:
- 为什么c1和c2的输出不同?
- 为什么需要通过调用在 c1 中重复 future 类型:
new CompletableFuture<Integer>()
首先要注意的是CompletableFuture
的方法(如thenApply
、thenAccept
等)return一个new CompletableFuture
实例。这形成了一种 "chain",其中每个新阶段都依赖于创建它的阶段——它的父阶段。当一个阶段正常或异常完成时,结果将被推送到其所有依赖的 未完成* 阶段(同一阶段可以有多个依赖阶段)。
* 正如您将在下面看到的,即使其父级尚未完成,您也可以完成一个阶段。如果父阶段完成并且当父阶段完成时,将不会调用 completed 依赖阶段,因为它已经完成。 对另一个问题的 简要介绍了这种情况的后果。
问题 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
转换前一阶段的结果并 returning 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
决定,并且在一定程度上由变量赋值的左侧决定。这意味着当您使用菱形运算符 (<>
) 时,您实际上是在使用以下内容:
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>
时,编译器就会知道 thenApply
阶段的输入类型 (T
) 是 Integer
,这意味着它知道 data
是一个 Integer
; Integer
可以乘以2
。
我有一个代码:
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);
输出:
20 40
问题:
- 为什么c1和c2的输出不同?
- 为什么需要通过调用在 c1 中重复 future 类型:
new CompletableFuture<Integer>()
首先要注意的是CompletableFuture
的方法(如thenApply
、thenAccept
等)return一个new CompletableFuture
实例。这形成了一种 "chain",其中每个新阶段都依赖于创建它的阶段——它的父阶段。当一个阶段正常或异常完成时,结果将被推送到其所有依赖的 未完成* 阶段(同一阶段可以有多个依赖阶段)。
* 正如您将在下面看到的,即使其父级尚未完成,您也可以完成一个阶段。如果父阶段完成并且当父阶段完成时,将不会调用 completed 依赖阶段,因为它已经完成。
问题 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
转换前一阶段的结果并 returning 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
决定,并且在一定程度上由变量赋值的左侧决定。这意味着当您使用菱形运算符 (<>
) 时,您实际上是在使用以下内容:
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>
时,编译器就会知道 thenApply
阶段的输入类型 (T
) 是 Integer
,这意味着它知道 data
是一个 Integer
; Integer
可以乘以2
。