第四章.使用闭包

简介:Groovy中的闭包就是去掉冗长无用代码的短小的匿名方法,闭包从函数式编程的Lambda表达式(指定了一个函数的参数与映射)派生而来。

一、闭包的便利性

groovy的闭包特性,大大简化了代码,而且其可以辅助轻量级、可复用的代码块。例子:

偶数相加:

/**

*传统代码一

*@paramn

*@return

*/

def sum(n) {

total =0

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

total += i

}

total

}

偶数相乘

/**

*传统代码二

*@paramn

*@return

*/

def product(n) {

prod =1

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

prod *= i

}

prod

}

偶数次幂

/**

*传统代码三

*@paramn

*@return

*/

def sqr(n) {

squared =1

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

squared<

}

squared

}

代码调用:

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)}"

结果:

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

可以发现:使用传统代码时,尽管方法及其相似,却不能以一种简单的方式将这些方法进行统一管理,代码冗余很大。在引进闭包后,这种问题迎刃而解。例子:

def  pickEvent(n,block) {

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

block(i)//此处为闭包作为参数传递后的代码

}

}

此时,一个带闭包参数的方法就能够解决很多问题:打印偶数、偶数求和、偶数乘积、偶数次幂 等等一类问题。例子:

打印偶数:

pickEvent(10,{number ->println number})//打印偶数,此处使用Lambda表达式

pickEvent(10,{println(it)})//打印偶数,当方法参数唯一时,可以在闭包调用时用it替换

pickEvent(10){println(it)}//打印偶数,当闭包是最后一个参数时,可以放于方法参数列表之后

结果都是:

2

4

6

8

10

偶数相加:

total =0

pickEvent(10) { total += it }

println total

偶数相乘:

pro =1

pickEvent(10){pro *= it }

println pro

偶数次幂:

sqrt =1

pickEvent(10){sqrt <

println sqrt

结果:

30

3840

1

这样的代码简洁大方,而且复用性强。但值得注意的是:Groovy的闭包不能单独存在,只能附到一个方法上,或者赋值给一个变量。

二、闭包的应用

普通方法在实现某个特定的目标明确的任务时要优于闭包,重构的过程是引入闭包的好时机。

闭包在使用时应该保持短小、有内聚性。闭包应该设计为附在方法上的小段代码,只有几行。

三、闭包的使用方式

由于Groovy的闭包不能单独存在,只能附到一个方法上,或者赋值给一个变量。所以其使用方式有两种:方法上作为参数,或者赋值给变量。例子:

引用闭包:

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)

结果:

total of event value from 1 to 10 is : 55

total of event value from 1 to 10 is : 55

四、向闭包传递参数

对于单个参数的闭包,it是该参数的默认名称,只要知道只传一个参数,就可以使用it,如果使用多个参数,就需要将参数一一列举出来。例子:

方法定义:

def tellFortune(closure){

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

}

代码调用:

tellFortune(){date,fortune->

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

}

结果:

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

因为Groovy是可选类型,所以上面调用方法的代码中添加参数类型,因此也可以这样写:

tellFortune(){Date date,fortune->

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

}

一般地,尽量为参数取一个贴切的名字,通常是可以避免定义类型的

五、使用闭包进行资源清理

Java采用自动垃圾回收机制,开发者不需要处理内存分配和释放。但是,不是所有资源都可以及时回收的,比如Java中写/读文件时需要关流;Android中,数据库相关操作时需要手动关闭cursor,bitmap用完时需要recycle:这些操作会不经意被开发者忘记。因此我们可以通过闭包,以及引进Execute Around Method模式,进行合理、简单的垃圾回收。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.)。例子:

类:

class Resource{

def open(){

println 'opening'

}

def read(){

println 'reading'

}

def write(){

println 'writing'

}

def close(){

println 'closing'

}

}

代码调用:

def resource=new Resource()

resource.open()

resource.read()

resource.write()

结果:

opening

reading

writing

我们发现,不调用close方法,是不能正常结束流程的。这时候需要借助闭包:将需要调用的代码以闭包作为参数放入一个静态方法,在这个静态方法中可以执行必须操作,闭包执行需求。例子:

在以上类中添加静态方法:

def static use(closure){

def r=newResource()

try{

r.open()

closure

}finally{

r.close()

}

}

代码调用:

Resource.use{re ->

re.read()

re.write()

}

结果:

opening

reading

writing

closing

六、闭包与协程

方法在执行过程中只有一个入口,方法完成后回到调用者的作用域,而协程支持多个入口,每个入口都是上次挂起调用的位置,我们可以进入一个函数,执行代码,挂起,再回到调用者的上下文或者作用域内执行一些代码。例子:

方法:

def iterate(n,closure){

1.upto(n){

println "In iterate with value${it}"

closure(it)

}

}

调用:

println 'Start..'

def total=1

iterate(4){

total+=it

println "In Closure with value${total}"

}

println 'Done..'

结果:

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..

七、科里化闭包

带有预绑定参数的闭包叫做科里化闭包,当对一个闭包调用curry()方法时,就要求绑定某些形参。在预先绑定了一个形参之后,调用闭包就不必再为这个参数传递实参()。例子:

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"

}

代码调用:

tellFortue(){date,fortune ->

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

}

结果:

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

八、动态闭包

可以确定一个闭包是否已经提供(布尔值判断),如果尚未提供,比如说一个算法,我们可以决定使用该方法的默认实现来代替调用者未能提供的特殊实现。例子:

方法:

def doSomthing(closure) {

if(closure) {

closure()

}else{

println 'No Closure provided for this method'

}

}

调用:

doSomthing(){

println 'A Closure was provided for this method'

}

doSomthing()

结果:

A Closure was provided for this method

No Closure provided for this method

在传递参数时也有很大灵活性,可以动态地确定一个闭包期望的参数数目和类型。在此基础上,我们可以使用闭包的maximumNumberOfParameters属性来判断并对不同值做出不同的实现。例子:

def computeOrders(int amount,Closure closure) {

def interst=0

if(closure.maximumNumberOfParameters==2) {

interst=closure(amount,0.2)

}else{

interst=closure(amount)

}

println interst

}

调用:

computeOrders(100) {it*0.1}

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

结果:

10.0

20.0

除了maximumNumberOfParameters属性外,闭包还有parameterTypes供开发者确定不同的实现。例子:

类:

def examine(Closureclosure){

println "$closure.maximumNumberOfParametersis Provided"

for(paraminclosure.parameterTypes) {

println param.name

}

println "--"

}

方法调用:

examine{}

examine{Dateval ->}

examine{val ->}

examine{inta,intb,doublec ->}

结果:

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

可以发现:调用一个空的闭包{}时,即没有参数时,默认返回一个Object类的参数。

九、闭包委托

this、owner、delegate是闭包的三个属性,用于确定哪个对象处理该闭包的方法调用,一般而言,delegate会设置为owner。闭包内this指向该闭包绑定的对象(正在执行的上下文),在闭包内引用的变量和方法都会绑定到this,如果this无法处理,转向owner,最后再转向delegate。

闭包内的方法执行顺序


十、使用尾递归编写程序

(略)

十一、使用记忆化改善性能

(略)

《完》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容