可完成的未来 - 完整的方法

Tho*_*ras 5 java java-8 completable-future

我有一个代码:

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);
Run Code Online (Sandbox Code Playgroud)

输出:

20 40

题:

  1. 为什么c1和c2之间的输出有差异?
  2. 为什么需要通过调用来重复c1中的未来类型:

新的CompletableFuture < 整数 >()

Sla*_*law 9

首先要注意的事情是,方法CompletableFuture(例如thenApply,thenAccept等)返回一个新的 CompletableFuture实例.这形成了一种"链",其中每个新阶段都依赖于它从其父阶段创建的阶段.当阶段完成时,无论是正常还是异常,结果都会被推送到其所有相关的未完成*阶段(同一阶段可以有多个依赖阶段).


*正如您将在下面看到的那样,即使其父母尚未完成,您也可以完成一个阶段.如果父级完成,则完成的依赖阶段将不会被调用,因为它已经完成.Holger对另一个问题的回答简要介绍了这种情况的后果.


问题1

在您的第一个示例中,您有以下内容:

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

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);
Run Code Online (Sandbox Code Playgroud)

c2是由舞台new CompletableFuture<>()的父母产生的thenApply舞台.所以现在当你打电话的时候c2.complete(20)你正在完成将值推送到的阶段thenApply.在Function随后通过转换乘以它的价值2,推动该结果thenAccept.这导致40打印到控制台.


问题2

您必须<Integer>在第一个示例中重复的原因是因为没有它,编译器无法推断第一个阶段的类型.签名thenApply是:

<U> CompletableFuture<U> thenApply(Function<? super T, ? extends U>)
Run Code Online (Sandbox Code Playgroud)

由此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);
Run Code Online (Sandbox Code Playgroud)

因为所有编译器都知道data它的类型是Object乘法无效; 一个Object不能乘以2.请注意,如果您只是将Functionfrom 更改为data -> data * 2to ,则上述内容将有效data -> 2(但显然这两个函数不相同).这是因为作业的左侧与结果有关thenApply,而不是new CompletableFuture<>().

当你明确指定<Integer>编译器然后知道阶段的输入类型(T),这意味着它知道是一个; 一个可以乘以.thenApplyIntegerdataIntegerInteger 2

  • 这些方法返回*new*期货的观点似乎是一个需要更多传播的方法.几个小时前我刚写了[相关问题的答案](/sf/answers/3844780271/).关于类型推断,编译器也可以使用函数和目标类型来确定`T`,例如当你编写`thenApply((整数数据) - >数据*2)`时,但这不会改变类型前面的`new CompletableFuture <>()`表达式,它自己推断出来.因此它仍会推断出"CompletableFuture <Object>"并抱怨不匹配的类型. (3认同)