日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Unmi學(xué)習(xí)Groovy之閉包

 sttx 2008-11-19

原創(chuàng) Unmi 學(xué)習(xí) Groovy 之閉包收藏

 | 舊一篇: Unmi 學(xué)習(xí) Groovy 之正則表達(dá)式

一. 認(rèn)識(shí)閉包

將代碼塊作為方法參數(shù)進(jìn)行傳遞,這種機(jī)制就叫做閉包。閉包可以引用在創(chuàng)建閉包的范圍中可見的變量。最近關(guān)于閉包的討論也比較多,閉包能使語(yǔ)言更具靈動(dòng)性,在動(dòng)態(tài)腳本語(yǔ)言中較廣泛的支持,如 Perl、Python、Ruby、JavaScript,還有我們的 Groovy。

有些語(yǔ)言能把函數(shù)作為參數(shù)傳遞,如 JavaScript 的回調(diào)函數(shù),Python,甚至是 C++ 的函數(shù)指針。而 Java 在這方面又略遜一籌,需搬動(dòng)一個(gè)匿名的內(nèi)部類來實(shí)現(xiàn)類似的功能,內(nèi)部類只能訪問外部聲明為 final 的變量。不過有呼聲要在 Java SE 7 中增加閉包特性,讓我們?cè)嚹恳源伞?/p>

Groovy 這回大概是從 Ruby 那兒偷得閉包的語(yǔ)法。前面說這么多,其實(shí)你看到了就會(huì)發(fā)現(xiàn),其實(shí)閉包很簡(jiǎn)單的,不信,請(qǐng)看:

Code   ViewCopyPrint
  1. logo = {   
  2.    println "Closure";   
  3. }   
  4. logo.call();   
  5. logo();  

用大括號(hào)括起來的,給它一個(gè)名字 logo 的那段就是一個(gè)閉包(有點(diǎn)像 Java 中的語(yǔ)句塊);閉包可以通過執(zhí)行它的 call() 方法調(diào)用,或者直接把它當(dāng)作一個(gè)常規(guī)方法對(duì)待。閉包也可以沒有名字,比如下面要講的集合方法中用的閉包。


二. 閉包的參數(shù)

閉包可以有參數(shù)。如果是一個(gè)參數(shù)的話,該參數(shù)就直接映射到名為 it 的變量,如:

Code   ViewCopyPrint
  1. discount = { it * 0.8}; //當(dāng)然你不想用 it 也行,那就指定了 { name -> name * 0.8 }   
  2. println discount(200);  //輸出 160.0  

如果是多個(gè)參數(shù)的閉包,則在閉包中用 "->" 把參數(shù)列表和實(shí)現(xiàn)隔開,如:(當(dāng)然一個(gè)參數(shù)也可以這么方式定義的)

Code   ViewCopyPrint
  1. totalPrice = {subtotal, tax, discount ->   
  2.     subtotal * discount * (1+tax);   
  3. }   
  4. println totalPrice(100,0.2,0.3); //輸出為 36.00  

我看到這里,怎么越來越覺得閉包那么像 C/C++ 中的宏定義呢?

調(diào)用閉包時(shí),如果參數(shù)數(shù)量不對(duì)會(huì)拋出 IncorrectClosureArgumentException;不過對(duì)于一個(gè)參數(shù)的閉包,可以少傳但不能多傳參數(shù),不傳參數(shù)時(shí),閉包認(rèn)為 it 為 null。


三. 閉包的傳遞

閉包在實(shí)現(xiàn)上是擴(kuò)展自 groovy.lang.Closure 類,因?yàn)樗鼈兪穷?,所以可以作為參?shù)傳遞給其他的方法,下面就來看一個(gè)例子:

Code   ViewCopyPrint
  1. class Handler{   
  2.     def action;           //用來保存一個(gè)閉包的實(shí)例   
  3.     def handle(object){   
  4.        action(object);    //調(diào)用閉包,并傳入?yún)?shù) object   
  5.     }   
  6. }   
  7.   
  8. //聲明兩個(gè)閉包,都是可接受一個(gè)參數(shù)   
  9. log = {object->println "Action occured: ${object}"};   
  10. save ={object->println "Object saved to the database: ${object}"};   
  11.   
  12. logHandler = new Handler(action:log);   //創(chuàng)建時(shí)初始化 action 為 log 閉包   
  13. saveHandler = new Handler(action:save); //創(chuàng)建時(shí)初始化 action 為 save 閉包   
  14.   
  15. obj = "Status changed";   
  16. logHandler.handle(obj);   //執(zhí)行 action 所指向的閉包 log   
  17. saveHandler.handle(obj);  //執(zhí)行 action 所指向的閉包 save  

輸出為:

Action occured: Status changed
Object saved to the database: Status changed

上面這種用法可用在事件觸發(fā)、回調(diào)操作或策略模式中。


四. 閉包與變量作用域

在 Groovy 中閉包可以訪問創(chuàng)建它的上下文中定義的變量,可在閉包中修改;而且閉包內(nèi)部定義的變量在周圍的上下文中也是可見的。看如下代碼片斷:

Code   ViewCopyPrint
  1. tax = 0.2;   
  2. c1 = {   
  3.     tax += 0.1;     //閉包中能訪問并修改外圍的變量   
  4.     discount = 0.2;   
  5. }   
  6.   
  7. c1();   
  8. println tax;   
  9. println discount;  //閉包中的變量在外圍也能訪問到  

執(zhí)行的結(jié)果是:

0.3
0.2


五. 閉包與集合操作

當(dāng)閉包與集合整合時(shí),它們展現(xiàn)了真正的威力。Groovy 中的 List、Map、String、和 Range 都有接受閉包參數(shù)的額外方法,譬如字符串也是字符的集合,也可以這么用。

涉及到的集合操作有 each、collect、inject、find、findAll、every、any。下面以例子來幫助理解這些方法的使用。

Code   ViewCopyPrint
  1. [1,2,3].each {print it+1}; //List 的 each 方法,輸出 234   
  2.   
  3. ["Name":"Unmi","Skill":"Java"].each{   
  4.     print "${it.key}: ${it.value} " //Map 的 each 方法,輸出 Name: Unmi Skill: Java   
  5. };    
  6.   
  7. "Groovy".each {print it.toUpperCase()};  //String 的 each 方法,輸出 GROOVY   
  8.   
  9. (1..3).each {print it+1};    //Range 的 each 方法,輸出 234  


注意:上面的閉包傳給方法,如果是在 GroovyShell 中逐行敲入代碼時(shí),起始花括號(hào)"{" 必須與調(diào)用方法在同一行上,比如說寫成下面的方式就有問題:

Code   ViewCopyPrint
  1. [1,2,3].each    
  2. {   
  3.     print it+1  
  4. }  

然而如果你想要在單獨(dú)一行上指定起始花括號(hào),可以使用下語(yǔ)法(調(diào)用方法后加個(gè)圓括號(hào)):

Code   ViewCopyPrint
  1. [1,2,3].each (   
  2.   {   
  3.       print it+1  
  4.   }   
  5. )  

要是寫在 .groovy 文件中或是在 GroovyConsole 中不受這個(gè)限制,但是為規(guī)范和不致?lián)Q個(gè)環(huán)境又出錯(cuò),還是應(yīng)該讓起起始花括號(hào)"{" 與調(diào)用方法在同一行。

其實(shí)怎么去理解這種要求呢?主要是兩點(diǎn):

其一是為什么我們通常不寫這個(gè)圓括呢?那是 Groovy 允許方法調(diào)用時(shí)省略圓括號(hào)

還有就是在 GroovyShell 下,如果輸完方法名,如 each,然后馬上回車,就報(bào)錯(cuò)

ERROR groovy.lang.MissingPropertyException: Exception evaluating property 'each' for java.util.ArrayList, Reason: groovy
.lang.MissingPropertyException: No such property: each for class: java.lang.Integer
at groovysh_evaluate.run (groovysh_evaluate:1)
...

只有方法名后面加個(gè)圓括號(hào),它才知道是參數(shù)列表開始,直至匹配的右圓括號(hào)結(jié)束,或是加了花括,它也知道是一個(gè)閉包的開始,直到匹配的花括號(hào)結(jié)束。



1) collect() 方法利用指定的閉包轉(zhuǎn)換集合的元素

Code   ViewCopyPrint
  1. println ([1,2,3].collect{it*2});  //輸出 [2,4,6]   

2) inject() 方法,可將前一次的迭代的結(jié)果傳遞給下一次迭代,請(qǐng)看例子:

Code   ViewCopyPrint
  1. [1,2,3].inject 0, {prevItem,item ->   
  2.     println "Previous: ${prevItem} - Current: ${item}"  
  3.     return item; //把當(dāng)前值返回作為下次迭代時(shí)的注入值   
  4. }  

輸出為:

Previous: 0 - Current: 1
Previous: 1 - Current: 2
Previous: 2 - Current: 3

在當(dāng)前迭代中能知道上一次所迭代的值。inject 接受兩個(gè)參數(shù),第一次迭代中使用的注入值,和將要使用的閉包。這個(gè)閉包也必須定義兩個(gè)參數(shù),分別為上次迭代的注入值和當(dāng)前的元素。注意在閉包中必須返回下次迭代中注入的值。否則,就會(huì)假設(shè)為 null。

為了現(xiàn)好的理解發(fā)這個(gè)注入語(yǔ)法,我們將上述例子分開來寫成如下:

Code   ViewCopyPrint
  1. list = [1,2,3]   
  2. closure = {prevItem,item ->   
  3.     println "Previous: ${prevItem} - Current: ${item}"  
  4.     return item; //把當(dāng)前值返回作為下次迭代時(shí)的注入值   
  5. }   
  6. list.inject(0,closure) //同時(shí)給 inject 方法調(diào)用參數(shù)框上圓括號(hào)(可選的)  

3) find() 方法找到符合閉包中所定義條件的第一次出現(xiàn)的元素,找不到則返回 null

Code   ViewCopyPrint
  1. println([2,5,7,9].find { it>5 }); //輸出為 7  

4) findAll() 方法是返回所有符合閉包中所定義條件的元素所組成的集合

Code   ViewCopyPrint
  1. println([2,5,7,9].findAll { it>5 }); //輸出為 [7, 9]  

5) every() 方法檢查集合中是否每一個(gè)元素都符合閉包中指定的條件,是則返回 true,否則為 false

Code   ViewCopyPrint
  1. println([2,5,7,9].every { it>1 }); //輸出為 true,如果閉中寫成 it>3 則輸出 false   

6) any() 方法則檢查集合中是否有一個(gè)元素符合閉包中指定的條件,有則返回 true,否則為 false

Code   ViewCopyPrint
  1. println([2,5,7,9].any { it>8 }); //輸出為 true  


參考:1. 《Java 腳本編程語(yǔ)言、框架與模式》第 4 章

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多