首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建Kotlin DSL - DSL语法Kotlin

如何创建Kotlin DSL - DSL语法Kotlin
EN

Stack Overflow用户
提问于 2017-09-08 10:00:42
回答 2查看 1.5K关注 0票数 11

安科一样,您可以编写如下回调函数:

代码语言:javascript
复制
alert {
    title = ""
    message = ""
    yesButton {
       toast("Yes") 
    }
    noButton { 
       toast("No")
    }
}

如何创建这样的嵌套函数?我试着像下面这样创建它,但似乎不起作用。

代码语言:javascript
复制
class Test {
    fun f1(function: () -> Unit) {}
    fun f2(function: () -> Unit) {}
}

现在,如果我在扩展函数中使用这个,

代码语言:javascript
复制
fun Context.temp(function: Test.() -> Unit) {
    function.onSuccess() // doesn't work
}

从活动中调用它:

代码语言:javascript
复制
temp {
    onSuccess {
        toast("Hello")
    }
}

不起作用。我在这里仍然缺乏一些基本概念。有人能在这里指路吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-09-08 10:52:34

Kotlin DSL

Kotlin很适合编写您自己的域特定语言,也称为型安全建筑商。正如您提到的,Anko库是使用DSL的一个示例。这里您需要理解的最重要的语言特性是“功能文字与接收者”,您已经使用了它:Test.() -> Unit

带接收基的函数字面值

Kotlin支持“带有接收者的函数文字”的概念。这允许在其正文中的函数文本的接收器上调用可见的方法,而不需要任何特定的限定符。这非常类似于扩展函数,在扩展函数中,也可以访问扩展内的接收方对象的成员。

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

代码语言:javascript
复制
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

正如您所看到的,这样一个带有接收方的函数文本在这里被看作是一个参数block。简单地执行这个block,并返回接收器(它是T的一个实例)。在行动中,如下所示:

代码语言:javascript
复制
val text: String = StringBuilder("Hello ").apply {
            append("Kotliner")
            append("! ")
            append("How are you doing?")
        }.toString()

StringBuilder用作接收方,并在其上调用applyblock作为参数在{}(lambda表达式)中传递,不需要使用附加限定符,只需多次调用append,这是StringBuilder的一个可见方法。

带有接收端DSL的函数文本

如果您从文档中看到这个示例,您将看到以下操作:

代码语言:javascript
复制
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 ),正如您在调用中看到的那样:

代码语言:javascript
复制
html {       // lambda with receiver begins here
    body()   // calling a method on the receiver object
}

你的例子

我创建了一个简单的示例,说明了您想要的内容:

代码语言:javascript
复制
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")
        }
    }
}
票数 14
EN

Stack Overflow用户

发布于 2017-09-08 12:16:46

在您的示例中,警报是返回某些类的函数,例如Alert。此外,此函数以接收方为参数函数文字。

在您的示例中,您应该使您的onSuccess成为测试类的成员方法,并且您的temp函数应该在不调用它的情况下返回测试类的实例。但是要像您所希望的那样调用to,它必须是onSuccess返回的任何类的成员函数。

我想你不太明白接收者的功能文字是如何工作的。当您玩得很开心(某样东西:A() ->单元)时,它意味着这个“东西”是A类的成员函数。

所以

你可以看看我的博客文章:如何为AsyncTask制作小型DSL

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46113833

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档