簡(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攒驰。
十蟆湖、使用尾遞歸編寫(xiě)程序
(略)
十一、使用記憶化改善性能
(略)
《完》