1.Groovy 中的閉包
Java的一些不足可以通過使用groovy的閉包很好的解決,通過下面這個例子來看看使用閉包的優勢:
在Java中遍歷一個集合的方法是使用迭代,就像下面這樣:
def acoll = ["Groovy", "Java", "Ruby"] for(Iterator iter = acoll.iterator(); iter.hasNext();){ println iter.next() }
實際上在 for 循環中並不需要類型聲明,因為 Groovy 已經將迭代轉變為任何集合的直接成員。在這個示例中,不必獲取 Iterator 實例並直接操縱它,可以直接在集合上迭代。而且,通常放在循環構造內的行為(例如 for 循環體中 println)接下來要放在閉包內。在深入之前,先看看如何執行這步操作。
對於上面的代碼,可以用更簡潔的方式對集合進行迭代,如下所示:
def acoll = ["Groovy", "Java", "Ruby"]
acoll.each{
println it
}
請注意,each 直接在 acoll 實例內調用,而 acoll 實例的類型是 ArrayList。在 each 調用之后,引入了一種新的語法 — {,然后是一些代碼,然后是 }。由 {} 包圍起來的代碼塊就是閉包。
閉包是可執行的代碼塊。它們不需要名稱,可以在定義之后執行。所以,在上面的示例中,包含輸出 it(后面將簡單解釋 it)的行為的無名閉包將會在 acoll 集合類型中的每個值上被調用。
在較高層面上,{} 中的代碼會執行三次,從而生成如圖所示的輸出。
Groovy
Java
Ruby
2聲明閉包
迄今為止,我們已經使用了閉包的簡單語法:在一個方法調用的后面,放置閉包代碼在一對花括號里,閉包的參數和代碼通過(->)進行分隔。
2.1簡單的聲明方式
下圖中顯示了閉包語法的簡單方式和一個新的特性,當只有一個參數傳遞的時候,這個參數是可選的,魔術變量it代替了聲明
log='' (1..10).each{counter -> log += counter } println log //12345678910 log='' (1..10).each{log += it } println log //12345678910
上面兩個閉包聲明是等價的,注意,不像counter,魔術變量it是不需要聲明的。
2.2 使用賦值的方式聲明閉包
第二種聲明閉包的方式是直接將它賦值給一個變量
def printer={ line -> println line}
閉包聲明在花括號中,並且賦給了printer變量
通過方法的返回值也是一種閉包的聲明方式
def Closure getPrinter(){ return {line -> println line} }
花括號指明構建了一個新的閉包對象,這個對象通過方法的調用返回
2.3引用一個方法作為閉包
第三種聲明閉包的方式是重用已有的聲明:一個方法。方法有一個方法體,可選的返回值,能夠接受參數,並且能夠被調用,於grooovy的閉包的相似性顯而易見,因此groovy可以重用你已經在方法中存在的代碼作為一個閉包,引用一個方法作為閉包使用reference.&操作符, reference是閉包調用時使用的對象實例,正如一個一般的方法調用使用reference.someMethod().
上圖顯示了閉包的調用方法。
下面代碼演示了方法閉包的使用
class MethodClosureSample{ int limit MethodClosureSample(int limit){ this.limit = limit } boolean validate(String value){ return value.length() <= limit } } MethodClosureSample first = new MethodClosureSample(6) MethodClosureSample second = new MethodClosureSample(4) def firstClosure = first.&validate; //方法作為閉包 def words=['Hello','Synvata','I','Love','you'] println words.findAll(firstClosure) //[Hello, I, Love, you] println words.findAll(second.&validate) //[I, Love, you]
3 比較
下面展示了創建和使用閉包的所有方式:使用簡單聲明、賦值變量和方法閉包
map=['a':1,'b':2,'c':3] /*直接把閉包作為參數傳遞,這是常用的方式*/ map.each{key,value -> map[key] = value * 2 } println map //[a:2, b:4, c:6] /*將閉包對象賦值給變量doubler*/ doubler={key,value -> map[key] = value * 2 } map.each(doubler) println map //[a:4, b:8, c:12] /*聲明一個普通的方法*/ def doubleMethod(entry){ map[entry.key]=entry.value * 2 } /*reference.&操作符引用方法作為一個閉包*/ doubler=this.&doubleMethod println doubler //org.codehaus.groovy.runtime.MethodClosure@3fb547 map.each(doubler) println map //[a:8, b:16, c:24]
4 調用閉包
假設有一個引用x指向一個閉包,我們能通過x.call()來調用閉包,或者簡單的x()
def col={println 'Hello, Groovy'} col(); //Hello, Groovy col.call() //Hello, Groovy
現在,我們來試試更復雜的一些東西,從一個方法的內部調用閉包
def benchmark(repeat,Closure worker){ start=System.currentTimeMillis() repeat.times{worker(it)} stop=System.currentTimeMillis() return stop-start } slow=benchmark(1000){ (int)it / 2 } fast=benchmark(1000){ it.intdiv(2) } println slow println fast
在benchmark中我們把閉包作為最后一個參數,這樣在調用該方法的時候就可以使用閉包的簡單語法聲明benchmark(){Closure}。方法參數中Closure聲明了閉包的類型,該聲明是可選的。repeat.times{worker(it)}根據repeat參數重復的調用閉包,並把當前重復的次數傳入閉包