疑惑
第一眼看到Gradle 的 Kotlin DSL我是不太能理解的 像下列這一段
application {
// Define the main class for the application.
mainClassName = "tw.elliot.App"
}
我能知道這用意是指定main class,但語法上,我沒辦法解釋。 看了一段時間,才大致上理解
其實,要這樣看
project.application({
mainClassName = "tw.elliot.App"
})
再來要說說我理解的過程
Function type 與 Lambda
要開始看kts設定前,一定要瞭解的是kotlin是有Function Type的
Function Type
- () -> Unit, 這Function Type沒有參數,也不回傳結果
- (String) -> Int, 這Function Type有一個String參數,回傳Int
- (String) -> () -> Int, 這Function Type有一個String參數,回傳一個會返回Int的Function
將變數設定為Function Type
var functionType1: () -> Unit
var functionType2: (String) -> Int
再來,定義Function Type實做
步驟1,利用匿名涵數
var functionType1: () -> Unit = fun() { println("IT'S FUNCTION TYPE 1!")}
var functionType2: (String) -> Int = fun(name:String): Int {
println("IT'S FUNCTION TYPE 2 for ${name}!")
return 1
}
步驟2,利用Lambda
var functionType1: () -> Unit = { println("IT'S FUNCTION TYPE 1!")}
var functionType2: (String) -> Int = { name:String ->
println("IT'S FUNCTION TYPE 2 for ${name}!")
1
}
var functionType3: (String) -> Int = { it ->
println("IT'S FUNCTION TYPE 3 for ${it}!")
1
}
var functionType4: (String) -> Int = {
println("IT'S FUNCTION TYPE 4 for ${it}!")
1
}
在此functionType3
利用了Kotlin的特例,當lambda僅有一個參數時,可用it
取代,此時連型別都不用宣告了
在functionType4
裡,連it都省了....
透過步驟1跟2,可以瞭解 function到Lambda 簡化的過程,這部份在Kotlin DSL裡會一直使用
Receiver
當我們想為Class增加新Function,在Kotlin裡可以這樣做
fun String.internalNewFunc(): Unit {
println("Test NewFunc with ${this} -- length [${this.length}]!")
}
此時的Receiver就是指String
,在function中可用this
取得receiver的實例。
上面的例子是為了String多了一個新的function,但用途不大,接下來我們可以配合function type,就可以有較明顯的作用
fun String.externalFuncPlug(funct: String.() -> Unit) {
this.funct()
}
我們把function type當做參數,送到了String當中,大概可以這樣用
fun main() {
var target: String = "Target"
target.externalFuncPlug {
println("Test NewFunc with ${this} -- length [${this.length}]!")
}
target.externalFuncPlug {
target = this.toUpperCase()
}
}
我們留下了一個口,讓新增的功能可以直接加進去,而不需要一直加寫新的function.
聰明如你,應該也想到了,如果將function type設為一個var 或 val,也能當參數傳入,不就更簡化了?
var exNewFunc: String.() -> Unit = {
println("Test NewFunc with ${this} -- length [${this.length}]!");
}
fun main() {
var target: String = "Target"
target.externalFuncPlug(exNewFunc)
}
整理
fun String.internalNewFunc(): Unit {
println("Test NewFunc with ${this} -- length [${this.length}]!")
}
fun String.externalFuncPlug(funct: String.() -> Unit) {
this.funct()
}
var exNewFunc: String.() -> Unit = {
println("Test NewFunc with ${this} -- length [${this.length}]!");
}
fun main() {
var target: String = "Target"
target.internalNewFunc()
target.externalFuncPlug {
println("Test NewFunc with ${this} -- length [${this.length}]!")
}
target.externalFuncPlug(exNewFunc)
}
執行結果都相同
Test NewFunc with Target -- length [6]!
Test NewFunc with Target -- length [6]!
Test NewFunc with Target -- length [6]!
再整理
kotlin當然想得到上面這個入口,所以直接定了一個apply()
做為讓外部加入新function的入口
var exNewFunc: String.() -> Unit = {
println("Test NewFunc with ${this} -- length [${this.length}]!");
}
fun main() {
var target: String = "Target"
target.apply(exNewFunc)
//再簡化,去掉(),直接寫下要做的事
target.apply {
println("Test NewFunc with ${this} -- length [${this.length}]!")
}
}
到這裡,才能看得懂DSL裡在做什麼
Project.application
實際翻下source code,果然發現這部份的DSL就是為了JavaApplication
增加新增操作
/**
* Configures the [application][org.gradle.api.plugins.JavaApplication] extension.
*/
fun org.gradle.api.Project.`application`(configure: org.gradle.api.plugins.JavaApplication.() -> Unit): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("application", configure)
這方式,加上一段自訂的function,並讓我們能取得JavaApplication
的變數,
而最初的plugins
plugins {
java
application
}
也就只是在為我們增加像上列application
的切入點而已。