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

时间:2018-01-19 22:39:46

标签: java lambda language-design completable-future

我正在学习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推断出(在编译期间)返回类型?然后它可以在编译时被赋予thenApplythenCompose - 类似的属性。

但我确信有一个很好的理由采用不同的方法,所以我想知道原因。

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

  • 是否因为存在单个方法确实不明确的情况,唯一的解决方案是使用单独的方法? (我想象的可能是,嵌套CompletableFuture或复杂的界面和泛型。)如果是这样,有人可以提供一个明确的例子吗?

  • 是出于其他原因还是有记录的推荐?

2 个答案:

答案 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的新手,我的建议是首先集中精力理解两件事:

  1. 类型参数的协方差/逆变(或者更确切地说是缺乏)。为什么List<String>不是List<Object>的子类型? List<Object>List<?>
  2. 之间的区别是什么?
  3. 类型擦除。为什么我不能基于通用参数重载方法?
  4. 在完成这些设置之后,研究如何通过反射解析类型变量(例如:了解Guava&#39;} TypeToken如何工作)

    编辑:修复了一个链接

答案 1 :(得分:1)

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

它们之间的区别是您在(result) -> { ... }部分中所隐藏的内容。

对于thenApply,您希望该函数返回一个String,以使整行返回一个CompleteableFuture<String>

对于thenCompose,您希望该函数返回一个CompleteableFuture<String>,以使整行返回一个CompleteableFuture<String>