在reduce()中使用扩展函数引用

时间:2018-08-14 07:43:49

标签: kotlin

假设:

val sets = listOf(setOf(1, 2, 3), setOf(2, 3, 4), setOf(3, 4, 5))

为什么在Kotlin 1.2中这是非法的?

val unionOfSets = sets.reduce(Set<Int>::union)  // == setOf(1, 2, 3, 4, 5)

这不应该吗?

val unionOfSets = sets.reduce { acc, set -> acc.union(set) }

来自https://kotlinlang.org/docs/reference/lambdas.html

  

带有和不带有接收器的功能类型的非文字值可以互换,因此接收器可以代表第一个参数,反之亦然。例如,可以在期望A.(B)-> C的地方传递或分配类型(A,B)-> C的值,反之亦然

1 个答案:

答案 0 :(得分:3)

Kotlin似乎没有在接收器上成功进行类型推断,这可能是由于所有模板所致(Kotlin对类型没有任何保证)。

但是您可以通过将引用传递给invoke可调用对象来解决:

fun main(args: Array<String>) {
    val sets = listOf(setOf(1,2,3), setOf(4,5,6), setOf(7,8,9))
    val unionOfSets = sets.reduce(Set<Int>::union::invoke)

    println(unionOfSets)
}

更新:这是sets.reduce(Set :: union)不起作用的原因

科特琳使用智能投射来解析接收者(请参阅第https://github.com/JetBrains/kotlin/blob/143c3ccb95f93299233ade88c24b2fa2b9b29abf/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/PSICallResolver.kt行第553页)

但是,只有在文档(https://kotlinlang.org/docs/reference/typecasts.html#smart-casts)中提到的类型可以保证的情况下,智能转换才能起作用:

  

请注意,当编译器无法保证变量不能在检查和用法之间更改时,智能强制转换将不起作用。

编译器尝试(通过智能强制转换)找到正确的接收者,但无法做到这一点,因为编译器无法保证acc变量不能更改。这是由于嵌套的泛型:您有一个List<Set<Int>>,它将在运行时为List<*>

这就是为什么我们可以改用invoke的原因。调用是在构建时生成的一种运算符方法,将是安全类型(无需智能强制转换)。