【问题标题】:What is a case where `thenApply()` vs. `thenCompose()` is ambiguous despite the return type of the lambda?尽管 lambda 的返回类型,`thenApply()` 与`thenCompose()` 是模棱两可的情况是什么?
【发布时间】:2018-01-19 22:39:45
【问题描述】:

我正在了解CompletableFutures。

我不是在问the difference between thenApply() and thenCompose()相反,我想问一个感觉不正确的代码“气味”,以及什么实际上可以证明它的合理性。

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

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

也不是这样:

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

要返回未来,您必须使用thenCompose(),否则使用thenApply()

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

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

  • 是因为在 Java 中从 lambda 推断返回类型很危险还是不可能? (我也是 Java 新手。)

  • 是不是因为有一种情况,单一的方法确实是模棱两可的,唯一的解决办法就是有不同的方法? (我在想可能是嵌套的CompletableFutures 或复杂的接口和泛型。)如果是这样,有人可以提供一个明确的例子吗?

  • 是出于其他原因还是记录在案的建议?

【问题讨论】:

  • thenDo() 如何替换这些方法,当它们不同时?在您的命名中,它们将是 thenDoNow()thenDoInBackground()。它们不一样。
  • "是因为在 Java 中从 lambdas 推断返回类型很危险还是不可能?" - 我不会说这是区分这两种方法的原因,但是是的,这是一个真实的陈述。 lambda 的类型(包括类型参数)是从其上下文中推断出来的,而不是相反。
  • @Andreas - 我想编译器应该已经知道基于 lambda 中返回的内容是 thenDoNow() 还是 thenDoBackground()...
  • @AndrewCheong 关于返回的内容是什么?你不是说传入的是什么吗?你是那个告诉方法想要它做什么的人。
  • @Andreas - 看来我完全误解了这些方法。谢谢。回到我的基础......投票自我关闭。

标签: java lambda language-design completable-future


【解决方案1】:

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

<U> CompletableFuture<U>   thenApply(Function<? super T,? extends U> fn)
<U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)

Function&lt;? super T,? extends U&gt;Function&lt;? super T,? extends CompletionStage&lt;U&gt;&gt;Function&lt;? super T, ?&gt; 作为一个共同的超类型(好吧,技术上它只是 Function)。

因此thenDo 的签名将类似于:

<U> CompletableFuture<U> thenDo(Function<? super T,?> fn)

虽然合法,但使用起来确实很痛苦,因为编译器无法检查 fn 的返回类型是否正确,并且必须接受任何内容。

此外,这个thenDo 的实现除了apply 函数之外别无选择,并检查返回的对象implements CompletionStage 是否(除了速度慢和......令人讨厌的不优雅)会有真正的问题在非直截了当的情况下:在 CompletableFuture&lt;CompletionStage&lt;String&gt;&gt; 上调用 thenDo 会发生什么?


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

  1. 类型参数的协变/逆变(或者说缺乏协变)。为什么List&lt;String&gt; 不是List&lt;Object&gt; 的子类型? List&lt;Object&gt;List&lt;?&gt; 有什么区别?
  2. 类型擦除。为什么不能重载基于泛型参数的方法?

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


编辑:修复了一个链接

【讨论】:

    【解决方案2】:

    您对差异的理解是错误的。 thenApply 和 thenCompose 都返回 CompletableFuture(或者,嗯,CompletionStages)。

    它们之间的区别在于您隐藏在 (result) -&gt; { ... } 部分中的内容。

    对于thenApply,您希望该函数返回String,以使整行返回CompleteableFuture&lt;String&gt;

    对于thenCompose,您希望该函数返回CompleteableFuture&lt;String&gt;,以使整行返回CompleteableFuture&lt;String&gt;

    【讨论】:

      猜你喜欢
      • 2020-02-25
      • 1970-01-01
      • 1970-01-01
      • 2011-09-07
      • 1970-01-01
      • 1970-01-01
      • 2011-10-04
      • 1970-01-01
      • 2018-06-01
      相关资源
      最近更新 更多