首页 > 解决方案 > 扩展 lambda 接受错误的参数

问题描述

我认为扩展 lambda 要求您传入正确的参数,但在下面的示例中我似乎没有这样做。

 

        open class Base {
        open fun f() = 1
    }
    class Derived : Base() {
        override fun f() = 99
    }
    fun Base.g(): Int { return f()}
    fun Base.h(xl: Base.() -> Int): Int { return xl()}
    
    fun main() {
        val b: Base = Derived() // Upcast
        println(b.g())
        println(b.h { f()}) // [1]
}

我知道 Base.h 采用一个以 Base 对象作为参数的函数。但是第 [1] 行显示它接受 f(),这是一个不带参数的函数。我一直在努力思考这个问题,并在它前面加上了 this.f() 并且它仍然有效。不服气,我修改代码如下:


    open class Base {
        open fun f() = 1
    }
    class Derived : Base() {
        override fun f() = 99
    }
    fun Base.g(): Int { return f()}
    fun Base.h(xl: (Base) -> Int): Int { return xl(Base())}
    fun test(i:Int) = 1
    fun main() {
        val b: Base = Derived() // Upcast
        println(b.g())
        println(b.h { test(1) })
    }

此代码有效。我已经运行它来验证。正如你所看到的,bh() 接受 test(),它接受一个 Int。这与 Base.h() 采用 Base 的事实相反。

你能解释一下吗?感谢您的阅读。

标签: functionkotlin

解决方案


注意传入的函数周围的大括号!他们改变了一切。

在第二个代码中,b.h { test(1) }没有函数传递testb.h. 传递test给的语法b.hb.h(::test)确实会产生您所期望的错误。

b.h { test(1) }传递一个以 a 作为参数的函数(一个lambda 表达式Base,忽略该参数,调用test(1)并返回结果。您基本上是将一个看起来像这样的函数传递给b.h

fun foo(p: Base) = test(1)

您可能想知道 Kotlin 是如何知道Base您根本没有Base在调用中写下这个词的。好吧,它可以只看 的声明b.h,并看到它{ test(1) }必须带一个参数Base

第一个代码片段有点不同,因为在这种情况下b.h接受 a Base.() -> IntBase.() -> Int表示接收器类型Base的函数func,即可以像 一样调用的函数someBaseObject.func()func将此与将对象作为参数的函数进行比较Base,可以像func(someBaseObject).

再次,{ f() }没有传递函数f。它是一个 lambda 表达式,除了调用之外什么都不做f。但是在这种情况下,f它本身可以传递给b.h( b.h(Base::f)),因为它一个接收器类型为Base!的函数。你可以someBaseObject.f(),不是吗?传递 lambda 类似于传递这样声明的扩展函数(您只是“包装”f在另一个函数中):

fun Base.foo() = f()

由于函数的接收者是Base,因此您可以访问lambda 中Base作为接收者(例如)的其他函数。f您还可以明确指定接收者(即this)。


推荐阅读