我正在学习CompletableFuture
s。
我不是在询问the difference between thenApply()
and thenCompose()
。相反,我想问一下感觉不对的代码“气味”,以及可能真正证明它的理由。
从我到目前为止看到的CompletableFuture
的用法来看,似乎你从来没有这样:
CompletableFuture<String> foo = getSomething().thenApply((result) -> { ... });
也不是这样:
String foo = getSomething().thenCompose((result) -> { ... });
要返回未来,您必须使用thenCompose()
,否则thenApply()
。
从经验来看,语言并没有设法消除每次都做出这种明确的选择,这似乎很奇怪。例如,是否有一个方法thenDo()
从lambda中的return
推断出(在编译期间)返回类型?然后它可以在编译时被赋予thenApply
或thenCompose
- 类似的属性。
但我确信有一个很好的理由采用不同的方法,所以我想知道原因。
是因为在Java中从lambda推断返回类型是危险还是不可能? (我也是Java的新手。)
是否因为存在单个方法确实不明确的情况,唯一的解决方案是使用单独的方法? (我想象的可能是,嵌套CompletableFuture
或复杂的界面和泛型。)如果是这样,有人可以提供一个明确的例子吗?
是出于其他原因还是有记录的推荐?
答案 0 :(得分:5)
作为参考,这两种方法的签名是:
<U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
Function<? super T,? extends U>
和Function<? super T,? extends CompletionStage<U>>
将Function<? super T, ?>
作为常见的超类型(好的,从技术上讲,它只是Function
)。
因此thenDo
的签名将类似于:
<U> CompletableFuture<U> thenDo(Function<? super T,?> fn)
虽然合法,但使用起来确实很痛苦,因为编译器无法检查fn
的返回类型是否正确并且必须接受任何内容。
此外,这个thenDo
的实现没有其他选择,只能apply
该函数,并检查返回的对象implements CompletionStage
,(除了缓慢而且......令人反感)不优雅的)在非直截了当的情况下会有真正的问题:在thenDo
上调用CompletableFuture<CompletionStage<String>>
时会发生什么?
如果您是java generics的新手,我的建议是首先集中精力理解两件事:
List<String>
不是List<Object>
的子类型? List<Object>
和List<?>
在完成这些设置之后,研究如何通过反射解析类型变量(例如:了解Guava&#39;} TypeToken
如何工作)
编辑:修复了一个链接
答案 1 :(得分:1)
您对差异的理解是错误的。 thenApply和thenCompose都返回CompletableFuture的(或者,当然是CompletionStages)。
它们之间的区别是您在(result) -> { ... }
部分中所隐藏的内容。
对于thenApply
,您希望该函数返回一个String
,以使整行返回一个CompleteableFuture<String>
。
对于thenCompose
,您希望该函数返回一个CompleteableFuture<String>
,以使整行返回一个CompleteableFuture<String>
。