简介: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。
十、使用尾递归编写程序
(略)
十一、使用记忆化改善性能
(略)
《完》