閉包,英文叫 Closure,是 Groovy 中非常重要的一個(gè)數(shù)據(jù)類型或者說(shuō)一種概念了。閉包的歷史來(lái)源,種種好處我就不說(shuō)了。我們直接看怎么使用它!
閉包,是一種數(shù)據(jù)類型,它代表了一段可執(zhí)行的代碼。其外形如下:
def aClosure = {//閉包是一段代碼,所以需要用花括號(hào)括起來(lái)..
String param1, int param2 -> //這個(gè)箭頭很關(guān)鍵。箭頭前面是參數(shù)定義,箭頭后面是代碼
println "this is code" //這是代碼,最后一句是返回值,
//也可以使用 return,和 Groovy 中普通函數(shù)一樣
}
簡(jiǎn)而言之,Closure 的定義格式是:
def xxx = {paramters -> code} //或者
def xxx = {無(wú)參數(shù),純 code} 這種 case 不需要->符號(hào)
說(shuō)實(shí)話,從 C/C++ 語(yǔ)言的角度看,閉包和函數(shù)指針很像。閉包定義好后,要調(diào)用它的方法就是:
閉包對(duì)象.call(參數(shù)) 或者更像函數(shù)指針調(diào)用的方法:
閉包對(duì)象(參數(shù))
比如:
aClosure.call("this is string", 100) 或者
aClosure("this is string", 100)
上面就是一個(gè)閉包的定義和使用。在閉包中,還需要注意一點(diǎn):
如果閉包沒(méi)定義參數(shù)的話,則隱含有一個(gè)參數(shù),這個(gè)參數(shù)名字叫 it,和 this 的作用類似。it 代表閉包的參數(shù)。
比如:
def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
等同于:
def greeting = { it -> "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
但是,如果在閉包定義時(shí),采用下面這種寫法,則表示閉包沒(méi)有參數(shù)!
def noParamClosure = { -> true }
這個(gè)時(shí)候,我們就不能給 noParamClosure 傳參數(shù)了!
noParamClosure ("test") <==報(bào)錯(cuò)喔!
1.省略圓括號(hào)
閉包在 Groovy 中大量使用,比如很多類都定義了一些函數(shù),這些函數(shù)最后一個(gè)參數(shù)都是一個(gè)閉包。比如:
public static <T> List<T> each(List<T> self, Closure closure)
上面這個(gè)函數(shù)表示針對(duì) List 的每一個(gè)元素都會(huì)調(diào)用 closure 做一些處理。這里的 closure,就有點(diǎn)回調(diào)函數(shù)的感覺(jué)。但是,在使用這個(gè) each 函數(shù)的時(shí)候,我們傳遞一個(gè)怎樣的 Closure 進(jìn)去呢?比如:
def iamList = [1,2,3,4,5] //定義一個(gè) List
iamList.each{ //調(diào)用它的 each,這段代碼的格式看不懂了吧?each 是個(gè)函數(shù),圓括號(hào)去哪了?
println it
}
上面代碼有兩個(gè)知識(shí)點(diǎn):
def testClosure(int a1,String b1, Closure closure){
//do something
closure() //調(diào)用閉包
}
那么調(diào)用的時(shí)候,就可以免括號(hào)!
testClosure (4, "test", {
println "i am in closure"
} ) //紅色的括號(hào)可以不寫..
注意,這個(gè)特點(diǎn)非常關(guān)鍵,因?yàn)橐院笤?Gradle 中經(jīng)常會(huì)出現(xiàn)圖 7 這樣的代碼:
http://wiki.jikexueyuan.com/project/deep-android-gradle/images/8.jpg" alt="" />
經(jīng)常碰見(jiàn)圖 7 這樣的沒(méi)有圓括號(hào)的代碼。省略圓括號(hào)雖然使得代碼簡(jiǎn)潔,看起來(lái)更像腳本語(yǔ)言,但是它這經(jīng)常會(huì)讓我 confuse(不知道其他人是否有同感),以 doLast 為例,完整的代碼應(yīng)該按下面這種寫法:
doLast({
println 'Hello world!'
})
有了圓括號(hào),你會(huì)知道 doLast 只是把一個(gè) Closure 對(duì)象傳了進(jìn)去。很明顯,它不代表這段腳本解析到 doLast 的時(shí)候就會(huì)調(diào)用 println 'Hello world!' 。
但是把圓括號(hào)去掉后,就感覺(jué)好像 println 'Hello world!'立即就會(huì)被調(diào)用一樣!
2.如何確定 Closure 的參數(shù)
另外一個(gè)比較讓人頭疼的地方是,Closure 的參數(shù)該怎么搞?還是剛才的 each 函數(shù):
public static <T> List<T> each(List<T> self, Closure closure)
如何使用它呢?比如:
def iamList = [1,2,3,4,5] //定義一個(gè) List 變量
iamList.each{ //調(diào)用它的 each 函數(shù),只要傳入一個(gè) Closure 就可以了。
println it
}
看起來(lái)很輕松,其實(shí):
我們能寫成下面這樣嗎?
iamList.each{String name,int x ->
return x
} //運(yùn)行的時(shí)候肯定報(bào)錯(cuò)!
所以,Closure 雖然很方便,但是它一定會(huì)和使用它的上下文有極強(qiáng)的關(guān)聯(lián)。要不,作為類似回調(diào)這樣的東西,我如何知道調(diào)用者傳遞什么參數(shù)給 Closure 呢?
此問(wèn)題如何破解?只能通過(guò)查詢 API 文檔才能了解上下文語(yǔ)義。比如下圖 8:
http://wiki.jikexueyuan.com/project/deep-android-gradle/images/9.jpg" alt="" />
圖 8 中:
each 函數(shù)說(shuō)明中,將給指定的 closure 傳遞 Set 中的每一個(gè) item。所以,closure 的參數(shù)只有一個(gè)。
對(duì) Map 的 findAll 而言,Closure 可以有兩個(gè)參數(shù)。findAll 會(huì)將 Key 和 Value 分別傳進(jìn)去。并且,Closure 返回 true,表示該元素是自己想要的。返回 false 表示該元素不是自己要找的。示意代碼如圖 9 所示:
http://wiki.jikexueyuan.com/project/deep-android-gradle/images/10.jpg" alt="" />
Closure 的使用有點(diǎn)坑,很大程度上依賴于你對(duì) API 的熟悉程度,所以最初階段,SDK 查詢是少不了的。