Kotlin基础——DSL

DSL(领域特定语言)

常见的DSL就是SQL和正则表达式,用于操作数据库和文本字符串,Kotlin DSL通常为嵌套的Lambda表达式或链式方法,如

带接收者的Lambda和扩展函数类型

对于普通的生成字符串函数,需要在Lambda中使用it指向StringBuilder实例

复制代码
fun buildString(builderAction: (StringBuilder) -> Unit): String {
    val sb = StringBuilder()
    builderAction(sb)
    return sb.toString()
}

val s = buildString {
    it.append("Hello ")
    it.append("World")
}

println(s)

转换为带接收者的Lambda可通过this或直接调用方法

复制代码
fun buildString(builderAction: StringBuilder.() -> Unit): String {
    val sb = StringBuilder()
    sb.builderAction()
    return sb.toString()
}

val s = buildString {
    this.append("Hello ")
    append("World")
}

println(s)

具体做法是使用扩展函数类型取代普通函数类型来声明参数的类型,将函数类型签名中的一个参数移到括号前面,并用一个.分割

复制代码
(StringBuilder) -> Unit		//一个接收StringBuild参数、无返回值的函数
StringBuilder.() -> Unit	//将(接收者对象)参数往前移

也声明一个扩展函数类型的变量

复制代码
val appendExcl: StringBuilder.() -> Unit = { this.append("!") }
val sb = StringBuilder("Hi")
sb.appendExcl()
println(sb)

Kotlin标准库中的apply和with就是利用扩展函数类型

复制代码
public inline fun <T> T.apply(block: T.() -> Unit): T {
   	.....
    block()			//apply的接收者被当作lambda的接收者
    return this		//返回接收者
}

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    ......
    return receiver.block()		//返回调用Lambda的结果
}

HTML构建器

用于Html的Kotlin DSL叫做HTML构建器,其是类型安全的

复制代码
open class Tag(val name: String) {
    private val children = mutableListOf<Tag>()
    protected fun <T : Tag> doInit(child: T, init: T.() -> Unit) {
        child.init()
        children.add(child)
    }

    override fun toString() = "<$name>${children.joinToString("")}</$name>"
}

fun table(init: TABLE.() -> Unit) = TABLE().apply(init)

class TABLE : Tag("table") {
    fun tr(init: TR.() -> Unit) = doInit(TR(), init)
}

class TR : Tag("tr") {
    fun td(init: TD.() -> Unit) = doInit(TD(), init)
}

class TD : Tag("td")

fun createTable() =
    table {
        tr {
            td {

            }
        }
    }

调用

复制代码
println(createTable())

<table><tr><td></td></tr></table>

invoke约定

重写invoke()可以让对象像函数一样调用,p(1)会被编译成p.invoke(1)

复制代码
class Person(val name: String) {
    operator fun invoke(age: Int) {
        println("$name,$age")
    }
}

val p = Person("A")
p(1)

Gradle中的DSL

复制代码
class DependencyHandler {
    fun compile(coordinate: String) {
        println("add dependency on $coordinate")
    }
    operator fun invoke(body: DependencyHandler.() -> Unit) {
        body()
    }
}

val dependencies = DependencyHandler()

dependencies.compile("com.demo.demo-lib:1.0.0")

dependencies {
    compile("com.demo.demo-lib:1.0.0")
}

中缀调用的DSL

对于下面的DSL

复制代码
infix fun <T> T.should(matcher: Matcher<T>) = matcher.test(this)

interface Matcher<T> {
    fun test(value: T)
}

class startWith(val prefix: String) : Matcher<String> {
    override fun test(value: String) {
        if (!value.startsWith(prefix)) {
            throw AssertionError("$value does not start with $prefix")
        }
    }

可使用中缀调用

复制代码
"kotlin" should startWith("kot")

"kotlin".should(startWith("kot"))

还可利用包装类进一步简化,利用obetject对象选择不同类型的should()重载方法

复制代码
object start
infix fun String.should(x: start): StartWrapper = StartWrapper(this)
class StartWrapper(val value: String) {
    infix fun with(prefix: String) =
        if (!value.startsWith(prefix))
            throw AssertionError("$value does not start with $prefix")
        else
            println("success")
}

"kotlin" should start with ("kot")

"kotlin".should(start).with("kot")

基本数据类型上定义扩展

复制代码
val Int.days: Period
    get() = Period.ofDays(this)

val Period.ago: LocalDate
    get() = LocalDate.now() - this

val Period.fromNow: LocalDate
    get() = LocalDate.now() + this

通过扩展函数实现获取一天前和一天后的日期

复制代码
println(1.days.ago)
println(1.days.fromNow)
相关推荐
luj_17683 小时前
从R语言想起的,。。。
服务器·c语言·开发语言·经验分享·算法
三道渊3 小时前
C语言:二级指针及void与void*的区别
c语言·开发语言
xiaoshiquan12063 小时前
Android16系统内容全屏,状态栏和导航栏透明
android
杜子不疼.3 小时前
Python + Ollama 本地跑大模型:零成本打造私有 AI 助手
开发语言·c++·人工智能·python
小此方3 小时前
Re:思考·重建·记录 现代C++ C++11篇 (一) 列表初始化&Initializer_List
开发语言·c++·stl·c++11·现代c++
计算机安禾3 小时前
【数据结构与算法】第29篇:红黑树原理与C语言模拟
c语言·开发语言·数据结构·c++·算法·visual studio
叹一曲当时只道是寻常4 小时前
Tauri v2 + Rust 实现 MCP Inspector 桌面应用:进程管理、Token 捕获与跨平台踩坑全记录
开发语言·后端·rust
Carson带你学Android4 小时前
编译更快、语法更香?一文看懂 Kotlin 2.3.20 的 6 大核心演进
android·kotlin
独特的螺狮粉4 小时前
开源鸿蒙跨平台Flutter开发:应对重症监护警报疲劳:BLoC 架构下的 FSM (有限状态机) 建模与全局消息干预机制
开发语言·flutter·华为·开源·harmonyos
jwn9994 小时前
Laravel3.x经典特性全解析
android