学起来:Kotlin DSL模拟gradle构建方法
简介
想要了解Kotlin DSL可以参考这个文章:Kotlin 之美—DSL篇
内网ata也有这样一篇文章:https://www.atatech.org/articles/138936
KotlinDSL是什么?能用来干什么?都可以从这两篇文章中获得答案。
而最近谷歌现在也在推Kotlin DSL来替代groovy作为gradle的语言。其实Kotlin设计的思路上主要受到了同为JVM语言的groovy、Scala影响,因此很多用法和写法都很相似。而Kotlin DSL, 则把 Kotlin 的语法糖演绎得淋漓尽致。学习Kotlin DSL可以帮助更好的理解Kotlin中的各种语法,用Kotlin DSL来模拟gradle的构建过程,则既可以学习Kotlin DSL,又复习下gradle的构建。
示例
首先看代码:
apply("android")
task {
println("debug task run")
}
task("test") {
println("test task run")
doFirst {
println("test task do first")
}
doLast {
println("test task do last")
}
}
dependencies {
compile("com.test.a")
implementation("com.test.b")
}
对于安卓开发者来说,这段代码会显得很熟悉,很像gradle中的配置,但有一些不同。但其实,这段代码并不是groovy,而是kotlin实现的代码,甚至这段代码并不是真正的gradle配置,而是我通过kotlin实现的一段mock gradle代码,这里面调用的所有方法都是调用了我自己实现的函数。代码链接 奉上,可以参考其中的方法实现。这段代码的运行结果是这样的:
---------------- project initialization ----------------
running in context: mock jvm
---------------- project configuration ----------------
abctest
apply plugin: java
debug task run
test task run
add compile dependency: com.test.a
add implementation dependency: com.test.b
---------------- project execution ----------------
configure dependency: com.test.a
configure dependency: com.test.b
execute task:
start executing...
executing task
execute task: test
start executing...
test task do first
executing task
test task do last
从输出结果来看,基本mock了gradle的三个步骤,有兴趣的同学可以对照gradle的构建流程理解一下,这里不做详细的介绍。
而通过这个示例可以看到,Kotlin DSL相比原API式的调用,结构更加清晰、更接近自然语言的风格。
实现细节
如何去实现这样一个接近自然语言的kotlin dsl?具体可以参考源码中的Project.kt, 下面介绍几个用的比较多的语言特性,包括:
- 带Receiver的lambda语句;
这种高阶函数的声明方式赋予了lambda类似作用域一样的空间,代码示例:
// 声明接收者
fun kotlinDSL(block:StringBuilder.()->Unit){
block(StringBuilder("Kotlin"))
}
// 调用高阶函数
kotlinDSL {
// 这个 lambda 的接收者类型为StringBuilder
append(" DSL")
println(this)
}
- 中缀调用;
在Kotlin中如果函数的参数只有一个,可以使用infix关键字来支持中缀调用。这种语法的支持让我们的代码从函数调用更加像自然语言,代码示例:
"key" to "value"
// 等价于
"key.to("value")
// to的声明,重点在infix关键字
infix fun Any.to(that:Any) = Pair(this,that)
- invoke约定操作符重载;
这种语法可以让你像调用函数一样调用对象,代码示例:
class Person(val name:String){
operator fun invoke(){
println("my name is $name")
}
}
>> val person = Person("geniusmart")
>> person()
my name is geniusmart
除了上面提到的三种语法糖,还有类似lambda参数、扩展函数等Kotlin语言特性也在DSL的实现中得到了使用,具体可以看代码,相信可以从中体会到很多Kotlin的妙用。也欢迎大家多多讨论更多Kotlin的用法。