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

时间:2019-03-01 00:10:43

标签: 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);

输出:

  

20 40

问题:

  1. 为什么c1和c2之间的输出存在差异?
  2. 为什么需要通过调用以下内容在c1中重复将来的类型:
  

新的CompletableFuture << strong>整数>()

1 个答案:

答案 0 :(得分:9)

首先要注意的是CompletableFuture的方法(例如thenApplythenAccept等)返回一个 new CompletableFuture实例。这形成了一种“链”,其中每个新阶段都依赖于从其创建的阶段(即其父阶段)。当一个阶段正常或异常完成时,结果将推入其所有相关的未完成 * 阶段(同一阶段可以有多个相关阶段)。


*正如您将在下面看到的那样,即使其父级尚未完成,您也可以完成一个阶段。如果并且父级完成,则 completed 依赖级将不会被调用,因为它已经完成。 answer by Holger简要介绍了另一个问题。


问题1

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

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

这里c1thenApply而非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);

此处c2new CompletableFuture<>()阶段的父级thenApply产生的阶段。因此,现在当您调用c2.complete(20)时,您将完成 root 阶段,该阶段会将值推入thenApplyFunction然后通过将值乘以2并将其结果推入thenAccept来转换值。这样会导致40被打印到控制台。


问题2

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

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

T由此CompletableFuture的类型(调用该方法的类型)确定。 UFunction确定,并且在一定程度上由变量赋值的左侧确定。这意味着当您使用菱形运算符(<>)时,您实际上在使用以下内容:

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。请注意,如果您仅将Functiondata -> data * 2更改为data -> 2,上述内容将是有效的(但显然这两个功能并不等效)。这是因为分配的左侧与thenApply而不是new CompletableFuture<>()的结果有关。

当您明确指定<Integer>时,编译器便知道T阶段的输入类型(thenApply)为Integer,这意味着它知道{{1} }是dataInteger 可以乘以Integer