【问题标题】:How to write lambdas with generics in kotlin?如何在 kotlin 中使用泛型编写 lambda?
【发布时间】:2017-12-06 10:04:22
【问题描述】:

我可以使用显式类型编写 lambdas id_Intid_Boolean。我可以用类型参数编写函数identity。我可以写带有类型参数的 lambdas 吗?

fun testFuncInt(f: (Int) -> Int): Int = f(1) + 2

val id_Int = { x: Int -> x }

fun testFuncBoolean(f: (Boolean) -> Boolean): Boolean = !f(false)

val id_Boolean = { x: Boolean -> x }

fun <T> identity(x: T) = x

fun main(args: Array<String>) {
    println(testFuncInt(id_Int))
    println(testFuncInt(::identity))
    println(testFuncBoolean(id_Boolean))
    println(testFuncBoolean(::identity))
}

【问题讨论】:

    标签: generics lambda kotlin type-parameter


    【解决方案1】:

    Kotlin 不支持声明泛型属性而不在类级别声明该类型 (see also),但您可以使用返回与所需类型对应的 lambda 的函数来实现:

    fun main(args: Array<String>) {
        println(testFuncBoolean(id()))
        println(testFuncInt(id()))
    }
    
    fun <T> id(): (T) -> T = { it }
    
    fun testFuncInt(f: (Int) -> Int): Int = f(1) + 2
    fun testFuncBoolean(f: (Boolean) -> Boolean): Boolean = !f(false)
    

    【讨论】:

    • “Kotlin 不支持诸如泛型属性之类的东西”我会改变这个措辞,因为这个陈述是不正确的。不过,不错且干净的解决方法。
    • 请注意,这不适用于suspending 函数,因为它们必须是 lambda,而不是匿名的。见discuss.kotlinlang.org/t/anonymous-suspending-functions/6000
    【解决方案2】:

    你不能用泛型编写 lambda,为什么下面的段落摘自官方文档说明了一切。

    lambda 表达式或匿名函数是“函数字面量”,即未声明但立即作为表达式传递的函数

    未声明 lambda 表达式或函数,它是一个匿名函数。

    但最终我们通过将函数类型声明为泛型来做同样的事情。我们可以传递一个 lambda 表达式来完成这项工作。

    fun testFuncInt(f: (Int) -> Int): Int = f(1) + 2 
    

    你可以这样称呼它:testFuncInt{ a -&gt; a }testFuncInt{ it }

    所以最后你在做同样的事情(带有类型参数的 lambdas),但没有类似的术语,因为 lambdas 是表达式或匿名函数。

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      不需要,但您通常不需要这样做。 lambda 没有声明(这是重点),所以它本质上是一个可以传递给函数的表达式,存储在 val/var 中,就像你做的那样 val id_Boolean = { x: Boolean -&gt; x } 但是类型确实可以像在表达式中一样被计算出来。

      此处的调用将解析为正确的类型,因为您的函数接受一个接受 Int 并返回 Int 的函数,以及一个接受 Boolean 并返回 Boolean 的函数

      testFuncInt({ x -> x }) // x is an Int
      testFuncInt({ it })     // it is the default argument
      testFuncInt { x -> x }  // as top one, Kotlin's syntactic sugar
      

      这里重要的是 lambda 表达式仍然提供类型安全性,即如果你做了这样的事情

      fun <T> foo(x: T, lambda: (T) -> Boolean): Boolean = lambda(x)
      foo(42, { x -> x })   // oops, lambda takes a T and returns a T
      

      这将触发编译器错误,因为 lambda 的类型与 foo 所期望的不匹配,即 foo 会将 Int 传递给 lambda,并且它期望返回 Boolean。另一方面,如果你这样做

      foo(123, { it == 42 })
      foo(123) { it == 42 }   // equivalent to above
      

      返回类型实际上被推断为Boolean,因为这是比较操作的结果,并且由于 123 和 42 属于同一类型,lambda 的类型实际上适合 foo预计。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-22
        • 1970-01-01
        • 2019-12-05
        • 1970-01-01
        • 1970-01-01
        • 2020-07-03
        • 2016-09-28
        • 2018-08-29
        相关资源
        最近更新 更多