11

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

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

4

2 回答 2

13

Kotlin DSL

Kotlin 非常适合编写自己的领域特定语言,也称为类型安全构建器。正如您所提到的,Anko 库是一个使用 DSL 的示例。您需要在这里了解的最重要的语言功能称为“带有接收器的函数文字”,您已经使用了它: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()

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

带有接收器的函数文字 - 在 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函数和属性而无需额外的限定符(如thiseg),如您在调用中所见:

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")
        }
    }
}
于 2017-09-08T10:52:34.680 回答
1

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

在您的示例中,您应该使 onSuccess 成为您的 Test 类的成员方法,并且您的 temp 函数应该返回 Test 类的实例而不调用它。但是要按照您的意愿调用 toast,它必须是 onSuccess 返回的任何类的成员函数

我认为您并不完全了解带有接收器的功能文字是如何工作的。当你玩得开心时(something : A.() -> Unit) 这意味着这个“东西”是 A 类的成员函数。

所以

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

于 2017-09-08T12:16:46.960 回答