第四章.使用閉包

簡(jiǎn)介:Groovy中的閉包就是去掉冗長(zhǎng)無(wú)用代碼的短小的匿名方法贪薪,閉包從函數(shù)式編程的Lambda表達(dá)式(指定了一個(gè)函數(shù)的參數(shù)與映射)派生而來(lái)媳禁。

一、閉包的便利性

groovy的閉包特性画切,大大簡(jiǎn)化了代碼损话,而且其可以輔助輕量級(jí)、可復(fù)用的代碼塊槽唾。例子:

偶數(shù)相加:

/**

*傳統(tǒng)代碼一

*@paramn

*@return

*/

def sum(n) {

total =0

for(inti =2; i <= n; i +=2) {

total += i

}

total

}

偶數(shù)相乘

/**

*傳統(tǒng)代碼二

*@paramn

*@return

*/

def product(n) {

prod =1

for(inti =2; i <= n; i +=2) {

prod *= i

}

prod

}

偶數(shù)次冪

/**

*傳統(tǒng)代碼三

*@paramn

*@return

*/

def sqr(n) {

squared =1

for(inti =2; i <= n; i +=2) {

squared<

}

squared

}

代碼調(diào)用:

println "the sum of even number from 1 to 10 is${sum(10)}"

println "the production of even number from 1 to 10 is${product(10)}"

println "the squares of even number from 1 to 10 is${sqr(10)}"

結(jié)果:

the sum of even number from 1 to 10 is 30

the production of even number from 1 to 10 is 3840

the squares of even number from 1 to 10 is 1

可以發(fā)現(xiàn):使用傳統(tǒng)代碼時(shí),盡管方法及其相似光涂,卻不能以一種簡(jiǎn)單的方式將這些方法進(jìn)行統(tǒng)一管理庞萍,代碼冗余很大。在引進(jìn)閉包后忘闻,這種問(wèn)題迎刃而解钝计。例子:

def ?pickEvent(n,block) {

for(inti =2; i <= n; i +=2) {

block(i)//此處為閉包作為參數(shù)傳遞后的代碼

}

}

此時(shí),一個(gè)帶閉包參數(shù)的方法就能夠解決很多問(wèn)題:打印偶數(shù)齐佳、偶數(shù)求和私恬、偶數(shù)乘積、偶數(shù)次冪 等等一類(lèi)問(wèn)題炼吴。例子:

打印偶數(shù):

pickEvent(10,{number ->println number})//打印偶數(shù)本鸣,此處使用Lambda表達(dá)式

pickEvent(10,{println(it)})//打印偶數(shù),當(dāng)方法參數(shù)唯一時(shí)硅蹦,可以在閉包調(diào)用時(shí)用it替換

pickEvent(10){println(it)}//打印偶數(shù)荣德,當(dāng)閉包是最后一個(gè)參數(shù)時(shí),可以放于方法參數(shù)列表之后

結(jié)果都是:

2

4

6

8

10

偶數(shù)相加:

total =0

pickEvent(10) { total += it }

println total

偶數(shù)相乘:

pro =1

pickEvent(10){pro *= it }

println pro

偶數(shù)次冪:

sqrt =1

pickEvent(10){sqrt <

println sqrt

結(jié)果:

30

3840

1

這樣的代碼簡(jiǎn)潔大方童芹,而且復(fù)用性強(qiáng)涮瞻。但值得注意的是:Groovy的閉包不能單獨(dú)存在,只能附到一個(gè)方法上假褪,或者賦值給一個(gè)變量署咽。

二、閉包的應(yīng)用

普通方法在實(shí)現(xiàn)某個(gè)特定的目標(biāo)明確的任務(wù)時(shí)要優(yōu)于閉包生音,重構(gòu)的過(guò)程是引入閉包的好時(shí)機(jī)宁否。

閉包在使用時(shí)應(yīng)該保持短小、有內(nèi)聚性缀遍。閉包應(yīng)該設(shè)計(jì)為附在方法上的小段代碼家淤,只有幾行。

三瑟由、閉包的使用方式

由于Groovy的閉包不能單獨(dú)存在絮重,只能附到一個(gè)方法上冤寿,或者賦值給一個(gè)變量。所以其使用方式有兩種:方法上作為參數(shù)青伤,或者賦值給變量督怜。例子:

引用閉包:

print 'total of event value from 1 to 10 is : '

println totalSelectValue(10,{it%2==0})

變量賦值:

print 'total of event value from 1 to 10 is : '

def isEven= {it%2==0}

println totalSelectValue(10,isEven)

結(jié)果:

total of event value from 1 to 10 is : 55

total of event value from 1 to 10 is : 55

四、向閉包傳遞參數(shù)

對(duì)于單個(gè)參數(shù)的閉包狠角,it是該參數(shù)的默認(rèn)名稱(chēng)号杠,只要知道只傳一個(gè)參數(shù),就可以使用it丰歌,如果使用多個(gè)參數(shù)姨蟋,就需要將參數(shù)一一列舉出來(lái)。例子:

方法定義:

def tellFortune(closure){

closure new Date("05/12/2017"),"Your day is fulled with ceremony"

}

代碼調(diào)用:

tellFortune(){date,fortune->

println "Fortune for${date} is ${fortune}"

}

結(jié)果:

Fortune for Fri May 12 00:00:00 CST 2017 is your day is fulled with ceremony

因?yàn)镚roovy是可選類(lèi)型立帖,所以上面調(diào)用方法的代碼中添加參數(shù)類(lèi)型眼溶,因此也可以這樣寫(xiě):

tellFortune(){Date date,fortune->

println "Fortune for${date} is ${fortune}"

}

一般地,盡量為參數(shù)取一個(gè)貼切的名字晓勇,通常是可以避免定義類(lèi)型的

五堂飞、使用閉包進(jìn)行資源清理

Java采用自動(dòng)垃圾回收機(jī)制,開(kāi)發(fā)者不需要處理內(nèi)存分配和釋放绑咱。但是绰筛,不是所有資源都可以及時(shí)回收的,比如Java中寫(xiě)/讀文件時(shí)需要關(guān)流描融;Android中铝噩,數(shù)據(jù)庫(kù)相關(guān)操作時(shí)需要手動(dòng)關(guān)閉cursor,bitmap用完時(shí)需要recycle:這些操作會(huì)不經(jīng)意被開(kāi)發(fā)者忘記窿克。因此我們可以通過(guò)閉包薄榛,以及引進(jìn)Execute Around Method模式,進(jìn)行合理让歼、簡(jiǎn)單的垃圾回收敞恋。Execute Around Method(查看(To represent pairs of actions that have to be taken together, code a method that takes a Block as an argument. Name the method by appending "During: aBlock" to the name of the first method to be invoked. In the body of the Execute Around Method, invoke the first method, evaluate the block, then invoke the second method.)。例子:

類(lèi):

class Resource{

def open(){

println 'opening'

}

def read(){

println 'reading'

}

def write(){

println 'writing'

}

def close(){

println 'closing'

}

}

代碼調(diào)用:

def resource=new Resource()

resource.open()

resource.read()

resource.write()

結(jié)果:

opening

reading

writing

我們發(fā)現(xiàn)谋右,不調(diào)用close方法硬猫,是不能正常結(jié)束流程的。這時(shí)候需要借助閉包:將需要調(diào)用的代碼以閉包作為參數(shù)放入一個(gè)靜態(tài)方法改执,在這個(gè)靜態(tài)方法中可以執(zhí)行必須操作啸蜜,閉包執(zhí)行需求。例子:

在以上類(lèi)中添加靜態(tài)方法:

def static use(closure){

def r=newResource()

try{

r.open()

closure

}finally{

r.close()

}

}

代碼調(diào)用:

Resource.use{re ->

re.read()

re.write()

}

結(jié)果:

opening

reading

writing

closing

六辈挂、閉包與協(xié)程

方法在執(zhí)行過(guò)程中只有一個(gè)入口衬横,方法完成后回到調(diào)用者的作用域,而協(xié)程支持多個(gè)入口终蒂,每個(gè)入口都是上次掛起調(diào)用的位置蜂林,我們可以進(jìn)入一個(gè)函數(shù)遥诉,執(zhí)行代碼,掛起噪叙,再回到調(diào)用者的上下文或者作用域內(nèi)執(zhí)行一些代碼矮锈。例子:

方法:

def iterate(n,closure){

1.upto(n){

println "In iterate with value${it}"

closure(it)

}

}

調(diào)用:

println 'Start..'

def total=1

iterate(4){

total+=it

println "In Closure with value${total}"

}

println 'Done..'

結(jié)果:

Start..

In iterate with value 1

In Closure with value 2

In iterate with value 2

In Closure with value 4

In iterate with value 3

In Closure with value 7

In iterate with value 4

In Closure with value 11

Done..

七、科里化閉包

帶有預(yù)綁定參數(shù)的閉包叫做科里化閉包睁蕾,當(dāng)對(duì)一個(gè)閉包調(diào)用curry()方法時(shí)苞笨,就要求綁定某些形參。在預(yù)先綁定了一個(gè)形參之后子眶,調(diào)用閉包就不必再為這個(gè)參數(shù)傳遞實(shí)參()瀑凝。例子:

def tellFortue(closure){

Date date=new Date("05/12/2017")

post Fortune=closure.curry(date)

postFortune "Your day is filled with ceremony"

postFortune "they are features ,not bug"

}

代碼調(diào)用:

tellFortue(){date,fortune ->

println "Fortune for${date}is${fortune}"

}

結(jié)果:

Fortune for Fri May 12 00:00:00 CST 2017 is Your day is filled with ceremony

Fortune for Fri May 12 00:00:00 CST 2017 is they are features ,not bugs

八、動(dòng)態(tài)閉包

可以確定一個(gè)閉包是否已經(jīng)提供(布爾值判斷)臭杰,如果尚未提供粤咪,比如說(shuō)一個(gè)算法模闲,我們可以決定使用該方法的默認(rèn)實(shí)現(xiàn)來(lái)代替調(diào)用者未能提供的特殊實(shí)現(xiàn)厌杜。例子:

方法:

def doSomthing(closure) {

if(closure) {

closure()

}else{

println 'No Closure provided for this method'

}

}

調(diào)用:

doSomthing(){

println 'A Closure was provided for this method'

}

doSomthing()

結(jié)果:

A Closure was provided for this method

No Closure provided for this method

在傳遞參數(shù)時(shí)也有很大靈活性仁期,可以動(dòng)態(tài)地確定一個(gè)閉包期望的參數(shù)數(shù)目和類(lèi)型。在此基礎(chǔ)上将塑,我們可以使用閉包的maximumNumberOfParameters屬性來(lái)判斷并對(duì)不同值做出不同的實(shí)現(xiàn)。例子:

def computeOrders(int amount,Closure closure) {

def interst=0

if(closure.maximumNumberOfParameters==2) {

interst=closure(amount,0.2)

}else{

interst=closure(amount)

}

println interst

}

調(diào)用:

computeOrders(100) {it*0.1}

computeOrders(100) {amount, interestRate -> amount*interestRate}

結(jié)果:

10.0

20.0

除了maximumNumberOfParameters屬性外,閉包還有parameterTypes供開(kāi)發(fā)者確定不同的實(shí)現(xiàn)蝌麸。例子:

類(lèi):

def examine(Closureclosure){

println "$closure.maximumNumberOfParametersis Provided"

for(paraminclosure.parameterTypes) {

println param.name

}

println "--"

}

方法調(diào)用:

examine{}

examine{Dateval ->}

examine{val ->}

examine{inta,intb,doublec ->}

結(jié)果:

1 param(s) is Provided

java.lang.Object

1 param(s) is Provided

java.util.Date

1 param(s) is Provided

java.lang.Object

3 param(s) is Provided

int

int

double

可以發(fā)現(xiàn):調(diào)用一個(gè)空的閉包{}時(shí)点寥,即沒(méi)有參數(shù)時(shí),默認(rèn)返回一個(gè)Object類(lèi)的參數(shù)来吩。

九敢辩、閉包委托

this、owner弟疆、delegate是閉包的三個(gè)屬性戚长,用于確定哪個(gè)對(duì)象處理該閉包的方法調(diào)用,一般而言怠苔,delegate會(huì)設(shè)置為owner同廉。閉包內(nèi)this指向該閉包綁定的對(duì)象(正在執(zhí)行的上下文),在閉包內(nèi)引用的變量和方法都會(huì)綁定到this柑司,如果this無(wú)法處理迫肖,轉(zhuǎn)向owner,最后再轉(zhuǎn)向delegate攒驰。

閉包內(nèi)的方法執(zhí)行順序


十蟆湖、使用尾遞歸編寫(xiě)程序

(略)

十一、使用記憶化改善性能

(略)

《完》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玻粪,一起剝皮案震驚了整個(gè)濱河市隅津,隨后出現(xiàn)的幾起案子诬垂,更是在濱河造成了極大的恐慌,老刑警劉巖饥瓷,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剥纷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡呢铆,警方通過(guò)查閱死者的電腦和手機(jī)晦鞋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)棺克,“玉大人悠垛,你說(shuō)我怎么就攤上這事∧纫辏” “怎么了确买?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)纱皆。 經(jīng)常有香客問(wèn)我湾趾,道長(zhǎng),這世上最難降的妖魔是什么派草? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任搀缠,我火速辦了婚禮,結(jié)果婚禮上近迁,老公的妹妹穿的比我還像新娘艺普。我一直安慰自己,他們只是感情好鉴竭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布歧譬。 她就那樣靜靜地躺著,像睡著了一般搏存。 火紅的嫁衣襯著肌膚如雪瑰步。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天璧眠,我揣著相機(jī)與錄音面氓,去河邊找鬼。 笑死蛆橡,一個(gè)胖子當(dāng)著我的面吹牛舌界,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泰演,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼呻拌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了睦焕?” 一聲冷哼從身側(cè)響起藐握,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤靴拱,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后猾普,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體袜炕,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年初家,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了偎窘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡溜在,死狀恐怖陌知,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掖肋,我是刑警寧澤仆葡,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站志笼,受9級(jí)特大地震影響沿盅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纫溃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一腰涧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧皇耗,春花似錦南窗、人聲如沸揍很。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窒悔。三九已至呜袁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間简珠,已是汗流浹背阶界。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留聋庵,地道東北人膘融。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像祭玉,于是被迫代替她去往敵國(guó)和親氧映。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容