如何在Kotlin中使用Type-Safe Builders?

时间:2018-01-16 16:43:49

标签: kotlin

我已经看过很多教程,但仍然没有得到它的确切运作方式。我理解主要的想法:一个函数保存带有数据的函数,但是看official documentation我无法知道数据的存储方式和位置以及谁调用负责其存储的函数。其他教程似乎只显示了一段代码,这对我没什么帮助。你能给我一个完整而简单的例子,一个琐碎的课程,比如一个人,好吗?

2 个答案:

答案 0 :(得分:3)

我也对一些细节感兴趣。这是我写的:

data class Person(
        var name: String? = null,
        var age: Int? = null,
        val children: MutableList<Person> = ArrayList()
) {
    fun child(init: Person.() -> Unit) = Person().also {
        it.init()
        children.add(it)
    }
}

fun person(init: Person.() -> Unit) = Person().apply { init() }

fun main(args: Array<String>) {
    val p = person {
        name = "Mommy"
        age = 33
        child {
            name = "Gugu"
            age = 2
        }
        child {
            name = "Gaga"
            age = 3
        }
    }
    println(p)
}

打印出来(添加了一些格式):

Person(name=Mommy, age=33, children=[
    Person(name=Gugu, age=2, children=[]), 
    Person(name=Gaga, age=3, children=[])
])

答案 1 :(得分:0)

Kotlin DSLs

Kotlin非常适合编写自己的特定领域语言,也称为type-safe builders。 Anko是使用此类DSL的示例之一。您需要了解的最重要的语言功能称为"Function Literals with Receiver",您已经使用了它:Test.() -> Unit

带接收器的函数文字 - 基础知识

Kotlin支持“带接收器的函数文字”的概念。这使我们能够在其主体中调用函数文字的接收器上的方法,而无需任何特定的限定符。这非常类似于扩展函数,其中也可以访问扩展中的接收者对象的成员。

一个简单的例子,也是Kotlin标准库中最重要的函数之一,是apply

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

如您所见,带接收器的这种函数文字在此处被视为参数block。简单地执行该块并返回接收器(它是T的实例)。在行动中,这看起来如下:

val foo: Bar = Bar().apply {
    color = RED
    text = "Foo"
}

我们实例化Bar的对象并在其上调用applyBar的实例成为“接收者”。在block(lambda表达式)中作为参数传递的{}不需要使用其他限定符来访问和修改显示的可见属性colortext

带接收器的函数文字 - 在DSL中

如果你看一下这个例子,取自文档,你会看到这一点:

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()函数需要这样一个带有HTML接收器的函数文字作为接收者。在函数体中,您可以看到它的使用方式:创建HTML的实例并在其上调用init

效益

这样的高阶函数的调用者期望带有接收器的函数文字(如html()),你可以使用任何可见的HTML函数和属性而无需额外的限定符(比如this eg),正如你在电话中看到的那样:

html {       // lambda with receiver begins here
    body()   // calling a method on the receiver object
}

实施例

我已经编写了一个示例DSL并在blog post中详细描述了它。也许这也很有用。