首页 > 解决方案 > 为什么 apply() 而不是函数调用

问题描述

下面的代码做同样的事情。函数trtd带有接收器对象的函数文字作为输入,以便在表中添加 tr 或 td 标记。

class TABLE : Tag("table") {
    fun tr(init: TR.() -> Unit) {
        children += TR().apply(init)
    }
}

class TR : Tag("tr") {
    fun td(init: TD.() -> Unit) {
        val td = TD()
        td.init()
        children += td
    }
}

我的问题是为什么我需要使用.apply()而不是:

class TABLE : Tag("table") {
    fun tr(init: TR.() -> Unit) {
        children += TR().init()
    }
}

我想这与编译器在 tr-object 中寻找init()有关。但这不应该在运行时决定吗?

标签: functionkotlinapplyinvoke

解决方案


正如我的评论中已经建议的那样,使用.apply您可以将调用 链接init在一起,+=因为apply返回其调用的目标。

如果您更喜欢使用init(),您可以使用 获得相同的结果

val tr = TR()
children += tr
tr.init()

链式变体的关键方面是应用function of the Kotlin's standard library is defined as an extension function of a generic typeT, accepting a *lambda with receiver作为其唯一参数,如您在此处看到的:

inline fun <T> T.apply(block: T.() -> Unit): T

为了解释它的含义,你可以自己实现这个函数:

fun <T> T.myApply(block: T.() -> Unit) : T {
    this.block()
    return this
}

以下示例模仿您的代码,使用假MyClass类型代替原始类型TR

fun <T> T.myApply(block: T.() -> Unit) : T {
    this.block()
    return this
}

class MyClass(val text: String) {
    fun foo() : Unit {
        println("foo $text")
    }
}

fun initializer(mc: MyClass) {
    println("initializer ${mc.text}")
    mc.foo()
}

fun run(init: MyClass.() -> Unit) {
    val result = MyClass("first").myApply(init)
    val x = MyClass("second")
    x.init()
}

fun main(args: Array<String>) {
    run(::initializer)
}

您可以使用此示例来遵循从run到的流程MyClass.foo,通过接受init作为带有接收器参数的 lambda的函数:我希望这可以帮助您澄清您对原始和替代实现的关键特征的理解tr


推荐阅读