最近学习了一下kotlin的DSL特性,感觉挺有趣的,可以极大的提升代码的可读性。DSL是domin specific language的缩写,中文名叫做领域特定语言,具体涉及到lambda表达式,invoke约定,中缀表达式,扩展函数这些技术细节。 如下所示,我们可以将代码写成这样,这段代码我们可以很清晰的看出执行了一个异步操作,在异步模块内,创建了一个json对象,然后再转到UI线程上执行。
doAsync {
val json = JsonNode {
"test1" to 1
"test2" to 2
"test3" to true
"test4" {
"key1" to 1.0
"key2" {
"key3" to 345
}
}
"test5" to array(1,2,3,4,5)
}
println(json.toString())
uiThread {
this.button.setText("执行完毕")
this.alert("哈哈哈哈哈")
}
}
1.具体实现
1.1 lambda表达式
kotlin当中的lambda表达式,支持当最后一个参数是方法时,可以直接写在括号外,如果只有一个方法参数时,可以省略括号。所以我们就可以写出这样的两个方法:
fun doAsync(block:()->Unit){
Thread(Runnable(block)).start()
}
fun uiThread(block: () -> Unit){
this.runOnUiThread(block)
}
一个是异步执行的方法,一个是在ui线程执行的方法,然后调用的时候就可以写成如下方式:
doAsync {
uiThread {
}
}
对比java,代码简洁了非常多
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
}).start();
1.2 invoke约定+中缀表达式+扩展函数
目前很多库都已经将DSL玩出了花,比如: html : https://github.com/Kotlin/kotlinx.html 可以非常直观的创建html,一目了然
html {
body {
div {
a("https://kotlinlang.org") {
target = ATarget.blank
+"Main site"
}
}
}
}
anko:https://github.com/Kotlin/anko 可以通过这种方式来写界面
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { toast("Hello, ${name.text}!") }
}
}
得到如下的界面 其实这些库的实现方式就是用了 invoke约定,中缀表达式以及扩展函数。
我尝试着使用DSL方式实现创建json对象. 首先分析一下:
- 一串json其实就是一个Map,key是字符串,value可以是字符串,整型等基础数据,也可以是一个数组或者json对象,所以我们需要一个Map<String, Any>来存储这些数据;
- 我们想要实现"test" : “value”,这种对应关系,所以需要实现一个中缀表达式,然后将这个对应关系转换成entry,添加到map里面;
- 想要实现如下的方式写json,所以我们得针对String写一个invoke方法,能够将String变成key,方法体里面的内容变成value,存到map里。
"test1" {
"key2" to 1.0
}
经过分析,我们可以写出如下代码:
class Node {
val body: HashMap<String, Any> = HashMap()
fun toJson(): JSONObject {
val jsonObject = JSONObject()
body.forEach {
if (it.value is Node) {
jsonObject.put(it.key, (it.value as Node).toJson())
} else {
jsonObject.put(it.key, it.value)
}
}
return jsonObject
}
inline operator fun String.invoke(block: Node.() -> Unit): Node {
val node = Node()
node.block()
this@Node.body[this] = node
return node
}
infix inline fun <T> String.to(value: T) {
this@Node.body[this] = value!!
}
}
fun JsonNode(block: Node.() -> Unit): JSONObject {
val node = Node()
node.block()
return node.toJson()
}
fun <T> array(vararg values: T): JSONArray {
val jsonArray = JSONArray()
for (value in values) {
jsonArray.put(value)
}
return jsonArray
}
使用方式如下:
JsonNode {
"test1" to 1
"test2" to 2
"test3" to true
"test4" {
"key1" to 1.0
"key2" {
"key3" to 345
}
}
"test5" to array(1,2,3,4,5)
}
然后我们就可以得到这样的json脚本
{
"test4": {
"key1": 1,
"key2": {
"key3": 345
}
},
"test5": [
1,
2,
3,
4,
5
],
"test2": 2,
"test3": true,
"test1": 1
}
|