一、引言
CompletableFuture类提供了使用函数式编程(JDK8函数式接口Function、Consumer、Predicate、Supplier)风格来编排异步处理逻辑的方案(异步编程利器:CompletableFuture详解 |Java 开发实战),下面对多数人容易混淆的thenApply和thenCompose详细说明。
二、函数签名
首先来看一下thenApply和thenCompose的函数签名:
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
thenApply和thenCompose的返回值都是CompletionStage<U>(CompletionStage是一个接口,CompletableFuture是CompletionStage接口的一个实现类),但入参不同,thenApply的入参是"Function<? super T,? extends U> fn",thenCompse的入参是"Function<? super T, ? extends CompletionStage<U>> fn"。
三、举例说明
为了便于理解,假设有如下业务场景:需要首先根据userId查询UserInfo,然后根据UserInfo查询UserOrder。下游提供查询接口如下:
public interface UserService {
CompletableFuture<UserInfo> getUserInfo(Long userId);
CompletableFuture<UserOrder> getUserOrder(UserInfo userInfo);
}
-
首先来看用thenApply的情形:
public CompletableFuture<UserOrder> getUserOrder(Long userId) { CompletableFuture<UserInfo> userInfoFuture = userService.getUserInfo(userId); CompletableFuture<CompletableFuture<UserOrder>> userOrderFuture = userInfoFuture.thenApply(userService::getUserOrder); return userOrderFuture.get(); }
-
thenApply的返回结果是一个嵌套的CompletableFuture,返回结果是需要通过get()或join()(join与get的区别)解开嵌套,比较麻烦;
-
为什么thenApply的返回结果是嵌套的CompletableFuture,可以结合thenApply的签名来看,签名"public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn)"中入参是一个函数式接口,该函数的返回值是U的子类。示例代码中thenApply的实际入参是"CompletableFuture<UserOrder> getUserOrder(UserInfo userInfo)",其返回值是"CompletableFuture<UserOrder>"(对应到thenApply的签名中,CompletableFuture<UserOrder>就是U的子类),而thenAppy的返回值是CompletionStage<U>,所以thenApply的返回结果是"CompletableFuture<CompletableFuture<UserOrder>>"(这种现象也叫CompletableFuture Nested)。
-
-
再来看使用thenCompose的情形:
public CompletableFuture<UserOrder> getUserOrder(Long userId) { CompletableFuture<UserInfo> userInfoFuture = userService.getUserInfo(userId); CompletableFuture<UserOrder> userOrderFuture = userInfoFuture.thenCompose(userService::getUserOrder); return userOrderFuture; }
-
thenCompose的返回结果是一个CompletableFuture(无嵌套),直接返回即可,比较方便;
-
那为什么thenCompose的返回结果是无嵌套的Completable呢?同样可以结合thenCompose的签名来看,签名"public <U> CompletionStage<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)"中入参是一个函数式接口,该函数的返回值是CompletionStage<U>的子类。示例代码中thenCompose的实际入参是"CompletableFuture<UserOrder> getUserOrder(UserInfo userInfo)",其返回值是"CompletableFuture<UserOrder>"(对应到thenCompose的签名中,CompletableFuture<UserOrder>就是CompletionStage<U>的子类,也就是说UserOrder是U的子类),而thenCompose的返回值是CompletionStage<U>,所以thenCompose的返回结果是"CompletableFuture<UserOrder>"(未产生Completable Nested现象)。
-
-
分析总结
-
从以上示例的分析可以看出,与thenApply相比,thenCompose将嵌套的CompletableFuture自动解开,避免了手动消除CompletableFuture嵌套,相当于是一个语法糖,本质和thenApply没有区别,都是用于连接异步任务完成流式计算。在示例场景中,因为被连接的任务是异步任务(返回值是CompletableFuture),所以使用thenCompose更方便;而如果被连接的任务是同步任务(返回值没被CompletableFuture包裹),使用thenApply更方便。
public interface UserService { CompletableFuture<UserInfo> getUserInfo(Long userId); UserOrder getUserOrder(UserInfo userInfo); } public CompletableFuture<UserOrder> getUserOrder(Long userId) { CompletableFuture<UserInfo> userInfoFuture = userService.getUserInfo(userId); CompletableFuture<UserOrder> userOrderFuture = userInfoFuture.thenApply(userService::getUserOrder); return userOrderFuture; }
-
"thenApply与thenCompose"和"map与flatMap"类似,flatMap可以将嵌套的流展平,thenCompose可以将嵌套的CompletableFuture展平。
-