Guy*_*uyT 91 java java-8 completable-future
我无法理解thenApply(和之间的区别thenCompose().
那么,有人可以提供有效的用例吗?
来自Java文档:
thenApply(Function<? super T,? extends U> fn)
Run Code Online (Sandbox Code Playgroud)
返回一个新的
CompletionStage,当该阶段正常完成时,将使用此阶段的结果作为所提供函数的参数执行.
thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
Run Code Online (Sandbox Code Playgroud)
返回一个新的
CompletionStage,当此阶段正常完成时,将使用此阶段作为所提供函数的参数执行.
我得到的第二个参数thenCompose扩展了CompletionStage,thenApply而不是.
有人可以提供一个例子,我必须使用thenApply何时使用thenCompose?
Joe*_*e C 134
thenApply 如果您具有同步映射功能,则使用此选项.
CompletableFuture<Integer> future =
CompletableFuture.supplyAsync(() -> 1)
.thenApply(x -> x+1);
Run Code Online (Sandbox Code Playgroud)
thenCompose如果你有一个异步映射函数(即返回一个CompletableFuture),则使用它.然后它将直接返回结果的未来,而不是嵌套的未来.
CompletableFuture<Integer> future =
CompletableFuture.supplyAsync(() -> 1)
.thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
Run Code Online (Sandbox Code Playgroud)
Did*_*r L 39
Java 9中更新的Javadoc可能有助于更好地理解它:
<U> CompletionStage<U> thenApply?(Function<? super T,? extends U> fn)
返回一个新的
CompletionStage,当该阶段正常完成时,将使用此阶段的结果作为所提供函数的参数执行.这种方法类似于
Optional.map和Stream.map.有关
CompletionStage特殊完成的规则,请参阅文档.
<U> CompletionStage<U> thenCompose?(Function<? super T,? extends CompletionStage<U>> fn)
Run Code Online (Sandbox Code Playgroud)
返回一个新的
CompletionStage,它与CompletionStage给定函数返回的值相同.当这个阶段正常完成时,调用给定的函数,将此阶段的结果作为参数,返回另一个
CompletionStage.当该阶段正常完成时,CompletionStage此方法返回的值将以相同的值完成.为确保进度,所提供的功能必须安排最终完成其结果.
这种方法类似于
Optional.flatMap和Stream.flatMap.有关
CompletionStage特殊完成的规则,请参阅文档.
小智 34
我认为@Joe C发布的回答是误导性的.
让我试着解释一下thenApply和thenCompose示例之间的区别.
我们假设我们有两种方法:getUserInfo(int userId)和getUserRating(UserInfo userInfo):
public CompletableFuture<UserInfo> userInfo = getUserInfo(userId)
public CompletableFuture<UserRating> getUserRating(UserInfo)
Run Code Online (Sandbox Code Playgroud)
两种方法返回类型都是CompletableFuture.
我们想先打电话getUserInfo(),在完成后,打电话getUserRating()给结果UserInfo.
在完成getUserInfo()方法时,让我们尝试两个thenApply和thenCompose.不同之处在于退货类型:
CompletableFuture<CompletableFuture<UserRating>> f =
userInfo.thenApply(this::getUserRating);
CompletableFuture<UserRating> relevanceFuture =
userInfo.thenCompose(this::getUserRating);
Run Code Online (Sandbox Code Playgroud)
thenCompose()就像ScalaflatMap那样可以平息嵌套的未来.
thenApply()按原样返回嵌套的期货,但是thenCompose()扁平嵌套,CompletableFutures以便更容易将更多的方法调用链接到它.
thenApply和thenCompose是的方法CompletableFuture。当你打算做些什么来使用他们CompleteableFuture的一个结果Function。
thenApply并且thenCompose都返回一个CompletableFuture作为自己的结果。您可以链接多个thenApply或链接thenCompose在一起。Function为每个调用提供a ,其结果将作为next的输入Function。
在Function你提供的,有时需要同步做一些事情。您的返回类型Function应为非Future类型。在这种情况下,您应该使用thenApply。
CompletableFuture.completedFuture(1)
.thenApply((x)->x+1) // adding one to the result synchronously, returns int
.thenApply((y)->System.println(y)); // value of y is 1 + 1 = 2
Run Code Online (Sandbox Code Playgroud)
其他时候,您可能希望在this中进行异步处理Function。在这种情况下,你应该使用thenCompose。您的返回类型Function应为CompletionStage。Function链中的下一个将获得该结果CompletionStage作为输入,从而展开CompletionStage。
// addOneAsync may be implemented by using another thread, or calling a remote method
// CompletableFuture<Integer> addOneAsync(int input);
CompletableFuture.completedFuture(1)
.thenCompose((x)->addOneAsync(x)) // doing something asynchronous, returns CompletableFuture<Integer>
.thenApply((y)->System.println(y)); // y is an Integer, the result of CompletableFuture<Integer> above
Run Code Online (Sandbox Code Playgroud)
这与Javascript的想法类似Promise。Promise.then可以接受返回值或值a Promise的函数。差异是由Java的通用擦除引起的。Function<? super T,? extends U> fn并且Function<? super T,? extends CompletionStage<U>> fn被视为相同的运行时类型- Function。因此thenApply,thenCompose必须被明确命名,否则Java编译器会抱怨相同的方法签名。最终结果是,Javascript Promise.then是由Java thenApply和thenComposeJava的两部分实现的。
如果您也对相关功能感到困惑,可以阅读我的其他答案thenApplyAsync。
thenCompose()更适合链接 CompletableFuture。
thenApply()更适合 Completable future 的转换结果。
您可以使用这两种技术来实现您的目标,但其中一种技术比其他技术更适合一种用例。
public CompletableFuture<Integer> process(Integer i) {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(
() -> new HeavyTask(i).execute());
return completableFuture;
}
@SneakyThrows
public CompletableFuture<Integer> thenApplyVsThenCompose() {
// each time calling thenApply() function returns CompletionState
// so you will get nested Futures
// you can think about it like map() java optional
CompletableFuture<Future<Integer>> cf1 = CompletableFuture.supplyAsync(
() -> new HeavyTask().execute())
.thenApply(i -> process(i));
// to get result you will have to get nested get() calls
Integer resultFromThenApply = cf1.get().get();
// when calling thenCompose() nested Futures are flatten
// you can think about it like flatMap() java optional
CompletableFuture<Integer> cf2;
cf2 = CompletableFuture.supplyAsync(
() -> new HeavyTask().execute())
.thenCompose(this::process);
// you have to just call one get() since thenCompose was flatten
Integer resultFromThenCompose = cf2.get();
return null;
}
Run Code Online (Sandbox Code Playgroud)
其他可以形象化两者之间差异的问题
当您不知道必须应用 thenApply()/thenCompose() 多少次(例如递归方法)时,您将如何实现解决方案?
public void nested() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> completableFutureToCompose = CompletableFuture.completedFuture(1);
for (int i = 0; i < 10; i++) {
log.info("Composing");
completableFutureToCompose = completableFutureToCompose.thenCompose(this::process);
}
completableFutureToCompose.get();
// not achievable using then apply
CompletableFuture<Integer> completableFutureToApply = CompletableFuture.completedFuture(1);
for (int i = 0; i < 10; i++) {
log.info("Applying");
completableFutureToApply = completableFutureToApply.thenApply(this::process).get();
}
completableFutureToCompose.get();
}
public CompletableFuture<Integer> process(Integer i) {
log.info("PROCESSING");
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(
() -> new HeavyTask(i).execute());
return completableFuture;
}
Run Code Online (Sandbox Code Playgroud)