如何创建Kotlin DSL - DSL语法Kotlin

时间:2017-09-08 10:00:43

标签: android kotlin kotlin-extension anko

anko一样,您可以编写如下的回调函数:

alert {
    title = ""
    message = ""
    yesButton {
       toast("Yes") 
    }
    noButton { 
       toast("No")
    }
}

如何创建这样的嵌套函数?我试着像下面那样创建它,但似乎没有工作。

class Test {
    fun f1(function: () -> Unit) {}
    fun f2(function: () -> Unit) {}
}

现在,如果我将它与扩展功能一起使用,

fun Context.temp(function: Test.() -> Unit) {
    function.onSuccess() // doesn't work
}

从活动中调用此内容:

temp {
    onSuccess {
        toast("Hello")
    }
}

不起作用。我在这里仍然缺乏一些基本概念。谁能在这里指导?

2 个答案:

答案 0 :(得分:12)

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。简单地执行此block并返回接收者(其为T的实例)。在行动中,这看起来如下:

val text: String = StringBuilder("Hello ").apply {
            append("Kotliner")
            append("! ")
            append("How are you doing?")
        }.toString()

StringBuilder用作接收器,并在其上调用applyblock作为{}(lambda表达式)中的参数传递,不需要使用其他限定符,只需多次调用append StringBuilderclass 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 }

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

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

html()

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

效益

这样的高阶函数的调用者期望带有接收器的函数文字(如HTML),你可以使用任何可见的this函数和属性而无需额外的限定符(比如html { // lambda with receiver begins here body() // calling a method on the receiver object } eg),正如你在电话中看到的那样:

class Context {
    fun onSuccess(function: OnSuccessAction.() -> Unit) {
        OnSuccessAction().function();
    }

    class OnSuccessAction {
        fun toast(s: String) {
            println("I'm successful <3: $s")
        }
    }
}

fun temp(function: Context.() -> Unit) {
    Context().function()
}

fun main(args: Array<String>) {
    temp {
        onSuccess {
            toast("Hello")
        }
    }
}

您的示例

我创建了一个你想要的简单例子:

<div> Item 1 <input name=item_description-0-item value=programme_title> 
</div>

答案 1 :(得分:1)

在您的示例中,alert是返回某个类的函数,例如Alert。 此函数也作为参数函数文字与接收器

在你的例子中,你应该做你的 onSuccess Test类的成员方法,你的temp函数应该返回Test类的实例而不调用它。 但是要按照你的愿望调用toast,它必须是onSuccess返回的任何类的成员函数

我认为你并不清楚接收器的功能文字是如何工作的。 当你玩得开心时(东西:A。() - &gt;单位)这意味着这个“东西”是A类的成员函数。

所以

您可以查看我的博文:How to make small DSL for AsyncTask