首页 文章

当尝试返回内联lambda时,Kotlin“期望没有参数”

提问于
浏览
4

我正在尝试编写一个Kotlin函数,它返回一个带参数的lambda . 我正在尝试使用以下代码来执行此操作:

fun <T> makeFunc() : (T.() -> Unit) {
    return { t: T ->
        print("Foo")
    }
}

注意:在实际程序中,函数更复杂并使用 t .

Kotlin拒绝此作为无效,在 t: T 给出'Expected no parameters'错误 .

但是,首先将此lambda分配给变量不会被拒绝并且工作正常:

fun <T> makeFunc() : (T.() -> Unit) {
    val x = { t: T ->
        print("Foo")
    }

    return x
}

这两个片段看起来完全相同,为什么会这样呢?在将 return 语句解释为lambda以外的其他语句后,是否大括号?

另外,IntelliJ告诉我变量的值可以内联,但这似乎会导致错误 .

enter image description here

2 回答

  • 5

    在Kotlin中,功能类型和lambda表达式的设计有一个奇怪的时刻 .

    实际上,可以在这两个语句中描述行为:

    • 功能类型的命名值可以在普通功能类型(如 (A, B) -> C )和相应类型的功能之间互换,第一个参数变为接收器 A.(B) -> C . 这些类型是assignable from each other .

    因此,当您声明一个类型为 (T) -> Unit 的变量时,您可以传递它或在需要 T.() -> Unit 的地方使用它,反之亦然 .

    但是,

    • Lambda表达式不能以这种自由的方式使用 .

    当一个带有接收器 T.() -> Unit 的函数被预期时,你不能在该位置放置一个参数为 T 的lambda,lambda应该与签名完全匹配,接收器和第一个参数不能相互转换:

    函数文字参数或函数表达式的形状必须与相应参数的扩展名完全匹配 . 您不能传递扩展函数文字或扩展函数表达式,其中函数是预期的,反之亦然 . 如果您确实想这样做,请更改形状,将文字指定给变量或使用as运算符 .

    (来自上面链接的文件)

    此规则使lambda更易于阅读:它们始终与预期类型匹配 . 例如,带接收器的lambda和带有隐式 it 的lambda之间没有任何歧义 .

    相比:

    fun foo(bar: (A) -> B) = Unit
    fun baz(qux: A.() -> B) = Unit
    
    val f: (A) -> B = { TODO() }
    val g: A.() -> B = { TODO() }
    
    foo(f) // OK
    foo(g) // OK
    baz(f) // OK
    baz(g) // OK
    
    // But: 
    
    foo { a: A -> println(a); TODO() } // OK
    foo { println(this@foo); TODO() } // Error
    
    baz { println(this@baz); TODO() } // OK
    baz { a: A -> println(a); TODO() } // Error
    

    基本上,这是IDE诊断错误 . 请report it作为Kotlin问题跟踪器的错误 .

  • 1

    您正在接收器 T 上定义函数类型 () -> Unit ,该函数确实没有参数,请参阅 "()" . 这个错误很有意义 . 由于您使用 T 作为接收器定义了函数类型,因此可以通过 this 引用 T

    fun <T> makeFunc(): (T.() -> Unit) {
        return {
           print(this)
        }
    }
    

相关问题