DSL 简述
相比于传统 API,DSL 更符合人类的语言习惯。
Domain Specific Language,专注于特定问题领域的计算机语言。
DSL 只是问题解决方案模型的外部封装,这个模型可能是一个 API 库、一个完整的框架等。
- HTML
对于 html 语言,kotlin 生态中 kotlinx.html
可在 web 应用中用于构建 html 的 DSL。
可以作为传统模版(JSP 等)等替代品。
kotlinx.html
分别提供了 kotlinx-html-jvm
和kotlinx-html-js
库的 DSL,用于在 JVM 和浏览器中直接使用 kotlin 代码来创建 html,释放了原有的 html 标签式的前端代码。
开发者可以使用 kotlin 编写传统意义的 html 页面了。
- Android
Kotlin 的 DSL 框架 Anko
是用于替代 Android 开发中布局 XML 文件的。
XML 本质上也是一种 DSL。
相比于 XML 风格的 DSL,Kotlin 原生的 DSL(Anko
)风格更简单、灵活。
- 内部 DSL,是基于通用编程语言实现的。
Anko
(Kotlin),Gradle
(Java)。 - 外部 DSL,从零开始构建的语言,需要实现语法分析器等,构建的难度相对较大。
外部 DSL 更加专注特定领域。
Kotlin 的 DSL 特性支持
Kotlin 中创建 DSL 主要用到了 3 个特性:
- 扩展函数、扩展属性
- 高阶函数(带接收者的 Lambda 表达式)
- invoke 函数调用约定 (invoke 操作符函数)
invoke
操作符函数
class Hello {
operator fun invoke(x: String): Unit {
println("Hello, $x !")
}
}
fun testInvoke(): Unit {
val hello = Hello()
// Hello实例的 invoke 方法调用(约定写法)
// 这个特性在一般程序代码中很少用到,而它使得DSL代码更加简洁
hello("Kotlin")
// 输出:Hello, Kotlin !
}
流式 Kotlin DSL
集合类的 流式 Kotlin DSL
/**
* 定义扩展函数
*/
fun <T: Comparable<T>> List<T>.sort() {
// this 代表调用该函数的对象,也就是函数接收者
Collections.sort(this)
}
// 测试 DSL
fun testListSort(): Unit {
val list = listOf(1,5,2,9,6)
list.sort() // 调用
println(list) // 输出:[1, 2, 5, 6, 9]
}
/**
* 定义扩展函数
*/
fun <T: Comparable<T>> List<T>.reverse() {
Collections.reverse(this)
}
// 测试 DSL
fun testListReverse(): Unit {
val list = listOf(1,5,2,9,6)
list.reverse() // 调用
println(list) // 输出:[6, 9, 2, 5, 1]
}
/**
* 定义扩展函数
*/
fun <T: Comparable<T>> List<T>.binarySearch(x: T): Int {
return Collections.binarySearch(this, x)
}
// 测试 DSL
fun testListBinarySearch(): Unit {
val list = listOf(1,5,2,9,6)
var index = list.binarySearch(1) // 调用
println(index) // 输出:0
index = list.binarySearch(2) // 调用
println(index) // 输出:2
}
文件读写的 流式 Kotlin DSL
需求:创建一个函数,输入文件名,输出文件中每行文本的集合。
// 文件读写 流式 API
fun readFileLines(fileName: String): List<String> {
return fileName.stream().buffered().reader("UTF-8").readLines()
}
// 测试
fun testReadFileLines(): Unit {
val fileName = "./EasyKt.iml"
println("==== $fileName : ====")
val lines = readFileLines(fileName)
lines.forEach(::println)
}
/**
* 1. 给 String 扩展一个 stream()函数
*/
fun String.stream(): FileInputStream {
return FileInputStream(this)
}
/**
* 2. 给 FileInputStream 扩展一个 buffered() 函数
*/
fun FileInputStream.buffered(): BufferedInputStream {
return BufferedInputStream(this)
}
/**
* 3. 给 BufferedInputStream 扩展一个 reader() 函数
*/
fun BufferedInputStream.reader(charset: String): InputStreamReader {
return InputStreamReader(this, charset)
}
/**
* 4. 给 Reader 扩展一个 readLines() 函数
*/
fun Reader.readLines(): List<String> {
val lines = arrayListOf<String>()
forEachLine {
lines.add(it)
}
return lines
}
集合类的 SQL风格 Kotlin DSL
fun testDSL_Sql(): Unit {
val students = listOf(Student("Jane","W",18,80),
Student("Jack","M",19,75),
Student("Lucy","W",18,85),
Student("Tyler","M",19,89),
)
// SQL 风格的过滤查询函数
val query = students.select().where {
it.score>79 }.and {
it.sex == "W"}
println(query)
// 输出:[Student(name=Jane, sex=W, age=18, score=80), Student(name=Lucy, sex=W, age=18, score=85)]
}
data class Student(var name:String, var sex: String, var age:Int, var score: Int)
/**
* 1. 给List 创建扩展函数 select()
*/
fun <E> List<E>.select(): List<E> = this
/**
* 2. 给List 创建高阶函数 where();
* where()函数的实现逻辑和 filter() 函数基本一致.
*/
fun <E> List<E>.where(predicate: (E) -> Boolean): List<E> {
val list = this;
val result = arrayListOf<E>()
for (e in list) {
if (predicate(e)) {
result.add(e)
}
}
return result
}
/**
* 3. 给List 创建高阶函数 and();
* and()函数的实现逻辑和 where() 函数一致,直接调用 where(predicate).
*/
fun <E> List<E>.and(predicate: (E) -> Boolean): List<E> {
return where(predicate)
}