Kotlin-小抄(十)Kotlin创建DSL
原创
©著作权归作者所有:来自51CTO博客作者wx5ba8dc11102bc的原创作品,请联系作者获取转载授权,否则将追究法律责任
DSL简介
- DSL (领域特定语言)指的是专注于特定问题领域的计算机语言,即对一个特定问题的方案模型更高层次的抽象表达,使之更加简单易懂。
- DSL只是问题解决方案模型的外部封装,这个模型可能是一个API库,也可能是一个完整的框架等
- 在Android中比较典型的例子使用DSL框架Anko来替代传统的xml(类似Flutter创建布局的方式)如下代码块:
UI {
// AnkoContext
verticalLayout {
padding = dip(30)
var title = editText {
// editText 视图
id = R.id.todo_title
hintResource = R.string.title_hint
}
var content = editText {
id = R.id.todo_content
height = 400
hintResource = R.string.content_hint
}
button {
// button 视图
id = R.id.todo_add
textResource = R.string.add_todo
textColor = Color.WHITE
setBackgroundColor(Color.DKGRAY)
onClick { _ -> createTodoFrom(title, content) }
}
}
}
DSL种类
内部DSL
- 内部DSL是指与项目中是使用的通用目的编程语言紧密相关的DSL
外部DSL
- 外部DSL是从零开始构建的语言,需要实现语法分析器,外部DSL与通用编程语言(GPL)类似,但是外部DSL更加专注于特定领域,创建 外部DSL和创建一种通用的百年城语言的过程是类似的,可以是编译型和解释型
Kotlin中的DSL支持
实现原理
- 扩展函数、扩展属性
- 带接收者的Lambda表达式(高阶函数)
- invoke函数调用约定
实现集合类的流式Kotlin DSL
(可以模仿filter来实现)
// filter函数的入参是一个函数predicate:(T)->Boolean public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> { return filterTo(ArrayList<T>(), predicate) }
fun <T : Comparable<T>> List<T>.sort() {
Collections.sort(this) // this代表调用函数的对象
}
@Test
fun testSort() {
val list = listOf(1,7,10,5,6)
list.sort()
print(list) // [1, 5, 6, 7, 10]
}
实现SQL风格的集合类DSL
data class Student(var name: String, var sex: String, var score: Int)
fun <E> List<E>.select(): List<E> = this
fun <E> List<E>.where(prediction: (E) -> Boolean): List<E> {
val list = this
val result = arrayListOf<E>()
for (e in list) {
if (prediction(e)) {
result.add(e)
}
}
return result
}
fun <E> List<E>.and(prediction: (E) -> Boolean) : List<E> {
return where(prediction)
}
@Test
fun testSql() {
val students = listOf(
Student("w", "M", 90),
Student("e", "M", 80),
Student("r", "M", 60),
Student("t", "M", 80),
Student("y", "F", 100)
)
val queryResult = students.select()
.where { it.score >= 80 }
.and {it.sex == "M"}
print(queryResult)
// [Student(name=w, sex=M, score=90), Student(name=e, sex=M, score=80), Student(name=t, sex=M, score=80)]
}