Kotlin 接收器函数用于 DSL 所需的解释
Explanation needed for Kotlin receiver function use for DSL
我正在尝试理解以下代码 (source)。
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
这里发生了什么?
谁能简单解释一下这是怎么回事?
这里是一步一步的解释:
1.创建函数,接收者类型 lambda。
fun html(init: HTML.() -> Unit): HTML {
这里的函数 html 接受类型为 HTML.() -> Unit
的参数 init
即表示它是 HTML 的接收者并且只能在真正的 HTML 对象。而: HTML
表示函数明明是returnsHTMLobject.
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:对于简单的语言,您可以将 html.init() 视为 init(html) 但 html 不是参数而是在 lambda
中作为 this
变量工作
这就是为什么您能够在该 lambda 上调用 body()
,因为隐含地您正在调用 this.body()
并且 this
来自 html.init()
的 html对象。
首先,让我们把这个例子简单一点,然后看看问题出在哪里。
我们可以像这样构建 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 使代码更加简洁,并允许我们使用一个很好的构建器语法。
我正在尝试理解以下代码 (source)。
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
这里发生了什么?
谁能简单解释一下这是怎么回事?
这里是一步一步的解释:
1.创建函数,接收者类型 lambda。
fun html(init: HTML.() -> Unit): HTML {
这里的函数 html 接受类型为 HTML.() -> Unit
的参数 init
即表示它是 HTML 的接收者并且只能在真正的 HTML 对象。而: HTML
表示函数明明是returnsHTMLobject.
2。在 html
调用 inithtml.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:对于简单的语言,您可以将 html.init() 视为 init(html) 但 html 不是参数而是在 lambda
中作为this
变量工作
这就是为什么您能够在该 lambda 上调用 body()
,因为隐含地您正在调用 this.body()
并且 this
来自 html.init()
的 html对象。
首先,让我们把这个例子简单一点,然后看看问题出在哪里。
我们可以像这样构建 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 使代码更加简洁,并允许我们使用一个很好的构建器语法。