閉包是Groovy的一個非常重要的特性肮柜,可以說他是DSL的基礎陷舅。閉包不是Groovy的首創(chuàng),但是它支持這一重要特性审洞,這就使用我們的代碼靈活莱睁、輕量、可復用芒澜,再也不用像Java一樣動不動就要搞一個類了仰剿,雖然Java后來有了匿名內部類,但是一樣冗余不靈活痴晦。
初識閉包
前面我們講過南吮,閉包其實就是一段代碼塊搂蜓,下面我們就一步步實現(xiàn)自己的閉包乍构,了解閉包的it變量的由來棍鳖。集合的each方法我們已經(jīng)非常熟悉了干签,我們就以其為例,實現(xiàn)一個類似的閉包功能砚尽。
task helloClosure << { //使用我們自定義的閉包
customEach {
println it
}
}
def customEach(closure){ //模擬一個有10個元素的集合施无,開始迭代
for(int i in 1..10){
closure(i)
}
}
在上面的例子中我們定義了一個方法customEach,它只有一個參數(shù)必孤,用于接收一個閉包(代碼塊)猾骡,那么這個閉包如何執(zhí)行呢?很簡單敷搪,跟一對括號就是執(zhí)行了兴想,會JavaScript的朋友是不是覺得很熟悉,把它當做一個方法調用赡勘,括號里的參數(shù)就是該閉包接收的參數(shù)嫂便,如果只有一個參數(shù),那么就是我們的it變量了闸与。
向閉包傳遞參數(shù)
上一節(jié)我們講了毙替,當閉包有一個參數(shù)時,默認就是it践樱;當有多個參數(shù)是厂画,it就不能表示了,我們需要把參數(shù)一一列出拷邢。
task helloClosure << { //多個參數(shù)
eachMap {k,v ->
println "${k} is ${v}"
}
}
def eachMap(closure){
def map1 = ["name":"張三","age":18]
map1.each {
closure(it.key,it.value)
}
}
從例子中我們可以看到袱院,我們?yōu)殚]包傳遞了兩個參數(shù),一個key瞭稼,一個value忽洛,便于我們演示。這是我們我們就不能使用it了环肘,必須要顯式的聲明出來脐瑰,如例子中的k,v廷臼,符號->用于把閉包的參數(shù)和主體區(qū)分開來苍在。
閉包委托
Groovy閉包的強大之處在于它支持閉包方法的委托。Groovy的閉包有thisObject荠商、owner寂恬、delegate三個屬性,當你在閉包內調用方法時莱没,由他們來確定使用哪個對象來處理初肉。默認情況下delegate和owner是相等的,但是delegate是可以被修改的饰躲,這個功能是非常強大的牙咏,Gradle中的很閉包的很多功能都是通過修改delegate實現(xiàn)的臼隔。
task helloDelegate << {
new Delegate().test {
println "thisObject:${thisObject.getClass()}"
println "owner:${owner.getClass()}"
println "delegate:${delegate.getClass()}"
method1()
it.method1()
}
}
def method1(){
println "Context this:${this.getClass()} in root"
println "method1 in root"
}
class Delegate {
def method1(){
println "Delegate this:${this.getClass()} in Delegate"
println "method1 in Delegate"
}
def test(Closure<Delegate> closure){
closure(this)
}
}
運行我們可以看到輸出:
thisObject:class build_e27c427w88bo0afju9niqltzf
owner:class build_e27c427w88bo0afju9niqltzf$_run_closure2
delegate:class build_e27c427w88bo0afju9niqltzf$_run_closure2
this:class build_e27c427w88bo0afju9niqltzf in root
method1 in root
this:class Delegate in Delegate
method1 in Delegate
通過上面的例子我們發(fā)現(xiàn),thisObject的優(yōu)先級最高妄壶,默認情況下摔握,優(yōu)先使用thisObject來處理閉包中調用的方法,如果有則執(zhí)行丁寄。從輸出中我們也可以看到這個thisObject其實就是這個構建腳本的上下文氨淌,他和腳本中的this對象是相等的。
從例子中也證明了delegate和owner是相等的伊磺,他們兩個的優(yōu)先級是owner要比delegate高盛正,所以對于閉包內方法的處理順序是thisObject>owner>delegate。
在DSL中屑埋,比如Gradle豪筝,我們一般會指定delegate為當前的it,這樣我們在閉包內就可以對該it進行配置摘能,或者調用其方法壤蚜。
task configClosure << {
person {
personName = "張三"
personAge = 20
dumpPerson()
}
}
class Person {
String personName int personAge
def dumpPerson(){
println "name is ${personName},age is ${personAge}"
}
}
def person(Closure<Person> closure){
Person p = new Person();
closure.delegate = p //委托模式優(yōu)先
closure.setResolveStrategy(Closure.DELEGATE_FIRST);
closure(p)
}
例子中我們設置了委托對象為當前創(chuàng)建的Person實例,并且設置了委托模式優(yōu)先徊哑,所以我們在試用person方法創(chuàng)建一個Person的實例時,可以在閉包里直接對該Person實例配置聪富,有沒有發(fā)現(xiàn)和我們在Gradle試用task創(chuàng)建一個Task的用法很像莺丑,其實在Gradle中有很多類似的用法,在Gradle也基本上都是使用delegate的方式使用閉包進行配置等操作墩蔓。
DSL
DSL(Domain Specific Language),領域特定語言梢莽,說白了就是專門關注某一領域專門語言,在于專奸披,而不是全昏名,所以才叫領域特定的,而不是像Java這種通用全面的語言阵面。
Gradle就是一門DSL轻局,他是基于Groovy的,專門解決自動化構建的DSL样刷。自動化構建太復雜仑扑、太麻煩、太專業(yè)置鼻,我們理解不了镇饮,沒問題,專家們就開發(fā)了DSL—Gradle箕母,我們作為開發(fā)者只要按照Gradle DSL定義的储藐,書寫相應的Gradle腳本就可以達到我們自動化構建的目的俱济,這也是DSL的初衷。
DSL涉及的東西還有很多钙勃,這里我們簡單的提一下概念蛛碌,讓大家有個了解,關于這方便更詳細的可以閱讀世界級軟件開發(fā)大師Martin Fowler的《領域特定語言》肺缕,這本書介紹的非常詳細左医。