如何创建 Kotlin DSL - DSL syntax Kotlin

How to create Kotlin DSL - DSL syntax Kotlin

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
}

从 Activity 调用:

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

无效。我在这里仍然缺乏一些基本概念。有人可以在这里指导吗?

Kotlin DSL

Kotlin 非常适合编写您自己的领域特定语言,也称为 type-safe builders. As you mentioned, the Anko library is an example making use of DSLs. The most important language feature you need to understand here is called "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()

A StringBuilder 被用作接收者并在其上调用 applyblock,在{}(lambda 表达式)中作为参数传递,不需要使用额外的限定符,只需多次调用appendStringBuilder 的可见方法。

带接收器的函数文字 - 在 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),正如您在调用中看到的那样:

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

你的榜样

我创建了一个您想要的简单示例:

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")
        }
    }
}

在您的示例中,警报是函数 returning 一些 class,例如警报。 此函数还以 receiver

作为参数函数文字

在你的例子中你应该让你的 onSuccess 测试 class 的成员方法,并且您的临时函数应该 return 测试 class 的实例而不调用它。 但是要按照您的意愿调用 toast,它必须是任何 class 由 onSuccess

编辑的 return 的成员函数

我认为您并不完全理解带接收者的函数式字面量是如何工作的。 当你有 fun(something : A.() -> Unit) 时,这意味着这个 "something" 是 A class.

的成员函数

所以

你可以看看我的博客 post : How to make small DSL for AsyncTask