一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

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

 sttx 2008-11-19

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

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

一. 認(rèn)識閉包

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

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

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

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

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


二. 閉包的參數(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ù)量不對會(huì)拋出 IncorrectClosureArgumentException;不過對于一個(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)部定義的變量在周圍的上下文中也是可見的??慈缦麓a片斷:

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í),起始花括號"{" 必須與調(diào)用方法在同一行上,比如說寫成下面的方式就有問題:

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

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

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)該讓起起始花括號"{" 與調(diào)用方法在同一行。

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

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

還有就是在 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è)圓括號,它才知道是參數(shù)列表開始,直至匹配的右圓括號結(jié)束,或是加了花括,它也知道是一個(gè)閉包的開始,直到匹配的花括號結(jié)束。



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

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

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

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è)注入語法,我們將上述例子分開來寫成如下:

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ù)框上圓括號(可選的)  

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 腳本編程語言、框架與模式》第 4 章

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产精品一区二区成人在线| 丰满熟女少妇一区二区三区 | 老司机激情五月天在线不卡| 国产精品午夜视频免费观看| 亚洲一区二区三区免费的视频| 日韩精品中文字幕亚洲| 欧美乱妇日本乱码特黄大片| 欧美日韩最近中国黄片| 国产免费无遮挡精品视频 | 亚洲人午夜精品射精日韩 | 不卡中文字幕在线免费看| 色婷婷亚洲精品综合网| 欧洲自拍偷拍一区二区| 国产不卡在线免费观看视频| 日韩精品中文字幕在线视频| 人妻巨大乳一二三区麻豆| 黄片免费播放一区二区| 日韩三级黄色大片免费观看| 亚洲一区二区精品免费| 色哟哟在线免费一区二区三区| 精品人妻一区二区三区免费| 色丁香之五月婷婷开心| 日本高清一道一二三区四五区| 九九热九九热九九热九九热 | 午夜亚洲少妇福利诱惑| 国产一级精品色特级色国产| 久久综合狠狠综合久久综合| 午夜福利视频日本一区| 男人和女人草逼免费视频| 人人妻人人澡人人夜夜| 日本不卡片一区二区三区| 国产成人免费激情视频| 日本欧美一区二区三区就| 亚洲国产综合久久天堂| 久久热在线免费视频精品| 欧美精品亚洲精品日韩专区| 欧洲一级片一区二区三区| 亚洲熟女诱惑一区二区| 色婷婷在线精品国自产拍| 日韩欧美一区二区黄色| 亚洲综合天堂一二三区|