尽管 lambda 的类型为 return,但 `thenApply()` 与 `thenCompose()` 不明确的情况是什么?
What is a case where `thenApply()` vs. `thenCompose()` is ambiguous despite the return type of the lambda?
我正在了解 CompletableFuture
s。
我不是在问 . 相反,我想问的是代码 "scent" 感觉不对,什么可能实际上证明它是合理的。
从我目前看到的 CompletableFuture
s 的用法来看,你似乎永远不会有这个:
CompletableFuture<String> foo = getSomething().thenApply((result) -> { ... });
也不是这个:
String foo = getSomething().thenCompose((result) -> { ... });
要return一个未来,就得用thenCompose()
,否则thenApply()
.
虽然根据经验,语言没有设计出一种方法来消除每次都做出这种明确的选择,这似乎很奇怪。例如,难道不能有一个方法 thenDo()
其 return 类型是从 lambda 中的 return
推断出来的(在编译期间)吗?然后它也可以在编译时被赋予 thenApply
或 thenCompose
之类的属性。
但我确信有一个很好的理由使用单独的方法,所以我想知道为什么。
是因为从 Java 中的 lambdas 推断 return 类型是危险的还是不可能的? (我也是 Java 的新手。)
是不是因为有一种情况,单一的方法确实会模棱两可,唯一的解决办法就是有单独的方法? (我想可能是嵌套的 CompletableFuture
或复杂的接口和泛型。)如果是这样,有人可以提供一个明确的例子吗?
是出于其他原因还是有据可查的建议?
供参考,两种方法的签名是:
<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 泛型的新手,我的建议是首先集中精力理解两件事:
- Covariance/contravariance 类型参数(或者说缺少)。为什么
List<String>
不是 List<Object>
的子类型? List<Object>
和 List<?>
有什么区别?
- 类型擦除。为什么我不能重载基于泛型参数的方法?
完成这些设置后,研究如何通过反射解析类型变量(例如:了解 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>
.
我正在了解 CompletableFuture
s。
我不是在问
从我目前看到的 CompletableFuture
s 的用法来看,你似乎永远不会有这个:
CompletableFuture<String> foo = getSomething().thenApply((result) -> { ... });
也不是这个:
String foo = getSomething().thenCompose((result) -> { ... });
要return一个未来,就得用thenCompose()
,否则thenApply()
.
虽然根据经验,语言没有设计出一种方法来消除每次都做出这种明确的选择,这似乎很奇怪。例如,难道不能有一个方法 thenDo()
其 return 类型是从 lambda 中的 return
推断出来的(在编译期间)吗?然后它也可以在编译时被赋予 thenApply
或 thenCompose
之类的属性。
但我确信有一个很好的理由使用单独的方法,所以我想知道为什么。
是因为从 Java 中的 lambdas 推断 return 类型是危险的还是不可能的? (我也是 Java 的新手。)
是不是因为有一种情况,单一的方法确实会模棱两可,唯一的解决办法就是有单独的方法? (我想可能是嵌套的
CompletableFuture
或复杂的接口和泛型。)如果是这样,有人可以提供一个明确的例子吗?是出于其他原因还是有据可查的建议?
供参考,两种方法的签名是:
<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 泛型的新手,我的建议是首先集中精力理解两件事:
- Covariance/contravariance 类型参数(或者说缺少)。为什么
List<String>
不是List<Object>
的子类型?List<Object>
和List<?>
有什么区别? - 类型擦除。为什么我不能重载基于泛型参数的方法?
完成这些设置后,研究如何通过反射解析类型变量(例如:了解 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>
.