尽管 lambda 的类型为 return,但 `thenApply()` 与 `thenCompose()` 不明确的情况是什么?

What is a case where `thenApply()` vs. `thenCompose()` is ambiguous despite the return type of the lambda?

我正在了解 CompletableFutures。

我不是在问 . 相反,我想问的是代码 "scent" 感觉不对,什么可能实际上证明它是合理的。

从我目前看到的 CompletableFutures 的用法来看,你似乎永远不会有这个:

CompletableFuture<String> foo = getSomething().thenApply((result) -> { ... });

也不是这个:

String foo = getSomething().thenCompose((result) -> { ... });

要return一个未来,就得用thenCompose(),否则thenApply().

虽然根据经验,语言没有设计出一种方法来消除每次都做出这种明确的选择,这似乎很奇怪。例如,难道不能有一个方法 thenDo() 其 return 类型是从 lambda 中的 return 推断出来的(在编译期间)吗?然后它也可以在编译时被赋予 thenApplythenCompose 之类的属性。

但我确信有一个很好的理由使用单独的方法,所以我想知道为什么。

供参考,两种方法的签名是:

<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 的 return 类型是否正确,并且必须接受任何内容。

此外,此 thenDo 的实现别无选择,只能 apply 函数并检查 returned 对象 implements CompletionStage,哪个(除了缓慢且...令人反感的不优雅)在非直接情况下会产生实际问题:在 CompletableFuture<CompletionStage<String>> 上调用 thenDo 时会发生什么?


如果您是 java 泛型的新手,我的建议是首先集中精力理解两件事:

  1. Covariance/contravariance 类型参数(或者说缺少)。为什么 List<String> 不是 List<Object> 的子类型? List<Object>List<?> 有什么区别?
  2. 类型擦除。为什么我不能重载基于泛型参数的方法?

完成这些设置后,研究如何通过反射解析类型变量(例如:了解 Guava 的 TypeToken 是如何工作的)


编辑: 修复了一个 link

您对差异的理解是错误的。 thenApply 和 thenCompose return CompletableFuture(或者,好吧,CompletionStages)。

它们之间的区别在于您在 (result) -> { ... } 部分隐藏了什么。

对于 thenApply,您希望该函数 return a String 使整行 return a CompleteableFuture<String>.

对于 thenCompose,您希望该函数 return a CompleteableFuture<String> 使整行 return a CompleteableFuture<String>.