CompleteFuture函数式编程中的thenApply和thenCompose

一、引言

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);
}
  1. 首先来看用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)。

  2. 再来看使用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现象)。

  3. 分析总结

    • 从以上示例的分析可以看出,与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展平。

四、参考资料

  1. CompletableFuture | thenApply vs thenCompose

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值