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