鍍金池/ 教程/ Android/ 閉包
Groovy 介紹
腳本類、文件 I/O 和 XML 操作
更多
一些前提知識
Gradle 工作流程
基本組件
題外話
總結
Gradle 編程模型及 API 實例詳解
閉包
Gradle 介紹
閑言構建
Groovy 中的數據類型

閉包

閉包

閉包的樣子

閉包,英文叫 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")  <==報錯喔!  

Closure 使用中的注意點

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
}

上面代碼有兩個知識點:

  • each 函數調用的圓括號不見了!原來,Groovy 中,當函數的最后一個參數是閉包的話,可以省略圓括號。比如
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
}

看起來很輕松,其實:

  • 對于 each 所需要的 Closure,它的參數是什么?有多少個參數?返回值是什么?

我們能寫成下面這樣嗎?

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 的參數只有一個。

  • findAll 中,絕對抓瞎了。一個是沒說明往 Closure 里傳什么。另外沒說明 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 查詢是少不了的。

上一篇:Groovy 介紹下一篇:總結