DSL使用Kotlin接收器功能所需的说明



我正在努力理解以下代码(源代码(。

class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML()  // create the receiver object
html.init()        // pass the receiver object to the lambda
return html
}
html {       // lambda with receiver begins here
body()   // calling a method on the receiver object
}

我真正抓不住的是这条线

html.init() // pass the receiver object to the lambda

这里发生了什么?

有人能简单地解释一下这里发生了什么吗?

首先,让我们让这个例子简单一点,看看问题是什么。

我们可以这样构建html函数:

fun html(init: (HTML) -> Unit): HTML {
val html = HTML()
init(html)
return html
}

这将更容易理解(一开始(,因为我们只是将一个常见的单参数lambda传递给html函数。

但现在的呼叫站点并不像建设者:

html { it: HTML -> // just for clarity     
it.body() // not very nice
}

如果我们可以在没有it的情况下在html内部调用body(),那不是很好吗?这是可能的!我们只需要一个带接收器的lambda。

fun html(init: HTML.() -> Unit): HTML { // <-- only this changed
val html = HTML()
init(html)
return html
}

看看html是如何像以前一样作为参数传递给init的?当然,我们也可以这样调用它:html.init(),如示例所示。HTML的实例在lambda的块内变为this

现在,我们可以这样做:

html {      
this.body()
}

由于this可以省略,我们到达这里:

html {      
body()
}

因此,最终,带有接收器的lambda使代码更加简洁,并允许我们使用良好的生成器语法。

以下是逐步解释:

1.创建函数,接收器类型lambda

fun html(init: HTML.() -> Unit): HTML {

函数html接受类型为CCD_ 13的参数CCD_。: HTML表明函数明显返回HTML对象。

2.在html调用init

html.init()

这里,init((函数被一个真正的HTML对象作为HTML的接收器调用。


好了,正式的谈话已经足够了,这就是接收器的含义:

因此,如果您还记得定义为fun A.myFun(...): ReturnType {}的扩展函数,那么在这种情况下,您会得到一个变量this,它充当被调用的类型a的实例

类似地,接收器lambda在里面给你一个this变量,

在一个特定的例子中:

class A {
fun thisCanBeCalledByAInstance() {
println("I've got called")
}
}
fun main() {
val receiver: A.() -> Unit = { // this: A
thisCanBeCalledByAInstance() // prints: I've got called
// or traditional way this.thisCanBeCalledByAInstance()
}
val a: A = A()
a.receiver()
}

在这里,您可以从A的实例调用方法(函数(,即使它是lambda,因为它是一个接收器。

PS:对于简单的langauge,您可以将html.init((视为init(html(,但html不是一个参数,而是在lambda中可用的this

这就是为什么您能够在该lambda上调用body(),因为隐式地调用this.body(),而this来自html.init()的html对象。

最新更新