Groovy闭包
1、Groovy中闭包基础
-
1.1 闭包的概念
闭包是被包装成对象的代码块,可以通过一个变量引用到它,也可以传递给别人进行处理(像处理一个对象一样处理闭包,比如作为参数传递、作为一个方法的返回值等) -
1.2 闭包的定义和调用
//定义一个闭包(闭包是一些代码组成的代码块对象,用{}括起来的一段代码)
def closure = { println 'Hello groovy!'}
//调用(类似定义了一个方法,然后可以去调用这个方法,可与方法对比着来理解闭包)
closure.call() //Hello groovy!
closure() //Hello groovy!
- 1.3闭包参数(普通参数和隐式参数)
//定义一个有参数的闭包 利用->区分参数和具体执行代码
def closure = { String name -> println "Hello $name!"}
//调用
closure('groovy') //Hello groovy!
//多个参数由逗号隔开
def closure2 = { String name,int age -> println "Hello $name! My age is $age"}
//调用
closure2('groovy',6) //Hello groovy! My age is 6
//当没有声明参数时,每个闭包都会有一个默认的参数it指代传入的参数
//只有一个参数时可考虑使用该方式声明闭包
//注意若声明了有一个参数,该闭包就没有这个it参数了!!
def closure3 = {println it}
closure3('hello groovy!') //hello groovy!
closure3([1,2,3]) //[1, 2, 3]
- 1.4 闭包的返回值
//闭包的返回值
def closure = { String name -> return "Hello $name!"}
def result = closure('groovy')
println result //Hello groovy! 返回值就是return 的内容
def closure1 = { println it}
def result1 = closure1("groovy")
println result1 //null ,所有的闭包都有返回值,若没有写返回,则返回null
2、Groovy中闭包使用(常用的四种)
- 2.1 与基本类型的结合使用
//只列出几项,更多的方法到DefaultGroovyMethods类中查看
def x = fab(5)
def x2 = fab2(5)
println x //120
println x2 //120
/**
* 求指定num的阶乘
*/
int fab(int number) {
def result = 1
//从1开始,依次递增到number,每次递增的结果传入闭包进行处理
1.upto(number, { num -> result *= num })
return result
}
/*
上面的是什么意思呢? num就是递增变化的那个参数 它从1~number
第一步,result = 1; num=1; 传入执行 result = result*num ==> result = 1*1 = 1
第二步,result = 1; num=2; 传入执行 result = result*num ==> result = 1*2 = 2
第三步,result = 2; num=3; 传入执行 result = result*num ==> result = 2*3 = 6
第四步,result = 6; num=4; 传入执行 result = result*num ==> result = 6*4 = 24
第五步,result = 24; num=5; 传入执行 result = result*num ==> result = 24*5 = 120
*/
/**
*求指定num的阶乘
*/
int fab2(int number) {
def result = 1
//从number开始,依次递减到1,每次递减的结果传入闭包进行处理
number.downto(1){ num -> result *= num }
return result
}
/*
同样的 num就是递减变化的那个参数 它从number~1
第一步,result = 1; num=5; 传入执行 result = result*num ==> result = 1*5 = 5
第二步,result = 5; num=4; 传入执行 result = result*num ==> result = 5*4 = 20
第三步,result = 20; num=3; 传入执行 result = result*num ==> result = 20*3 = 60
第四步,result = 60; num=2; 传入执行 result = result*num ==> result = 60*2 = 120
第五步,result = 120; num=1; 传入执行 result = result*num ==> result = 120*1 = 120
*/
//注意看fab2()方法中downto()方法的写法,当方法中的最后一个参数是闭包时,可以将闭包写在括号的外面,若该方法仅有一个闭包参数,除了可以将闭包写在外面,还可以将括号省略,如下所示
/**
* 累加 从0累加到(number-1)
* times方法始终从0开始到number-1循环
*/
int accumulate(int number) {
def result = 0
number.times { num -> result += num }
return result
}
def sum = accumulate(5)
println sum //0+1+2+3+4 = 10
//相信大家都可以理解上面的运行原理,可以自己按照上面的方式先推一遍,以便更好的理解闭包的使用
/*
同样的 num就是递增变化的那个参数 它从number~1
第一步,result = 0; num=0; 传入执行 result = result+num ==> result = 0+0 = 0
第二步,result = 0; num=1; 传入执行 result = result+num ==> result = 0+1 = 1
第三步,result = 1; num=2; 传入执行 result = result+num ==> result = 1+2 = 3
第四步,result = 3; num=3; 传入执行 result = result+num ==> result = 3+3 = 6
第五步,result = 6; num=4; 传入执行 result = result+num ==> result = 6+4= 10
*/
-
经过上面的推导之后,相信对于闭包的使用已经问题不大了,只要知道了与之结合使用的方法的运作方式,即可慢慢理解
-
2.2 与String的结合使用
def str = "the 2 add 3 is 5"
//each方法遍历str的每一个字符,都执行闭包中的操作,并返回它自己。每个字符都输出两遍
str.each { print it.multiply(2)} //tthhee 22 aadddd 33 iiss 55
//find方法查找符合条件的第一个,找到即停止并返回,没找到返回null
println str.find {String s -> return s.isNumber() } //2
//findAll方法查找所有符合条件的内容,返回所有符合条件的内容的集合
def listResult = str.findAll { s -> s.isNumber() }
println listResult.toListString() //[2, 3, 5] ,集合都可以转为listString的形式
//注意find和findAll方法闭包的返回值都需要是Boolean的,注意看findAll方法,闭包中的最后表达式有值时,即作为闭包的返回值,可省略return,参数类型可推导,省略
//any判断是否有符合条件的内容,有一个,即返回true,否则返回false
println str.any { s -> s.isNumber() } //true
//every方法判断是否所有的内容都符合条件,是返回true,否则返回false
println str.every { s -> s.isNumber() } //false
//collect方法对每一个元素都执行闭包中的操作,并将每一个操作过后的结果添加到一个新的ArrayList中
def list2 = str.collect { it.toUpperCase() }
println list2 //[T, H, E, , 2, , A, D, D, , 3, , I, S, , 5]
//题外话,对list的collect操作
//对list的collect操作,去除所有的空格并且转换为大写
println str.findAll {
String s -> !s.isBlank()}.collect { it.toString().toUpperCase() } // [T, H, E, 2, A, D, D, 3, I, S, 5]
- 2.3 与数据结构的结合使用
这部分内容放到讲解Groovy中的数据结构中
3、Groovy中闭包进阶
- 3.1、闭包的关键变量(this,owner,delegeta)
this: 代表定义闭包处的类
owner: 代表闭包定义处的类或者对象
delegeta: 可代表做任意的对象,默认与owner一致,可手动指定
/**
* 闭包中三个重要的变量:this,owner,delegate
*/
def scriptClosure = {
println "scriptClosure this:" + this
println "scriptClosure owner:" + owner
println "scriptClosure delegate:" + delegate
}
scriptClosure.call()
/*
* 输出结果
*/
//scriptClosure this:variable.ClosureStudy@3232a28a
//scriptClosure owner:variable.ClosureStudy@3232a28a
//scriptClosure delegate:variable.ClosureStudy@3232a28a
//可以看到它们都指向了ClosureStudy类对象(即定义它们的类或者说距离最近的那个封闭类)
//ps:ClosureStudy.groovy在编译后会在out目录下生成一个继承Script的java类(脚本文件)
//========对指向最近的内部类进一步说明============
//在ClosureStudy类中再定义了一个内部类
class InnerClass {
//定义了一个静态闭包
def static classClosure = {
println "classClosure this:" + this
println "classClosure owner:" + owner
println "classClosure delegate:" + delegate
}
//定义了一个静态方法
def static mTestMethod() {
//在方法中定义一个闭包
def methodClosure = {
println "methodClosure this:" + this
println "methodClosure owner:" + owner
println "methodClosure delegate:" + delegate
}
//调用
methodClosure.call()
}
}
//调用闭包和方法
def innerClass = new InnerClass()
innerClass.classClosure.call()
innerClass.mTestMethod()
/*
*输出结果
*/
//classClosure this:class variable.InnerClass
//classClosure owner:class variable.InnerClass
//classClosure delegate:class variable.InnerClass
//methodClosure this:class variable.InnerClass
//methodClosure owner:class variable.InnerClass
//methodClosure delegate:class variable.InnerClass
//可以看到它们都指向了定义它们的那个类的字节码,注意因为是静态的,所以指向的都是字节码(结果后面没有@xxx)。
/*
*去掉static关键字后运行结果
*/
//classClosure this:class variable.InnerClass@6cd28fa7
//classClosure owner:class variable.InnerClass@6cd28fa7
//classClosure delegate:class variable.InnerClass@6cd28fa7
//methodClosure this:class variable.InnerClass@6cd28fa7
//methodClosure owner:class variable.InnerClass@6cd28fa7
//methodClosure delegate:class variable.InnerClass@6cd28fa7
- 说了这么多,可以看到this,owner,delegete都是一样的值,那么对于owner的说明中,“指代定义闭包处的类或者对象”,指代“对象”又是怎么一回事呢?
//ClosureStudy中定义一个闭包
def nestClosure = {
//在闭包中再定义一个闭包
def innerClosure = {
println "innerClosure this:" + this
println "innerClosure owner:" + owner
println "innerClosure delegate:" + delegate
}
innerClosure.call()
}
nestClosure.call()
/*
*输出结果
*/
//innerClosure this:variable.ClosureStudy@6cd28fa7
//innerClosure owner:variable.ClosureStudy$_run_closure3@4fb3ee4e
//innerClosure delegate:variable.ClosureStudy$_run_closure3@4fb3ee4e
- 可以看到,this依然指向定义它的ClosureStudy类,但是owner和delegate却指向了ClosureStudy中的对象_run_closure3@4fb3ee4e。还记得闭包的概念吗?闭包是被包装成对象的代码块,闭包就是一个对象(Closure类型的对象),所以在闭包中定义的闭包的owner实际上指向了定义它的那个闭包对象,而delegate的指向默认与owner一致,所以他也指向了定义闭包的那个闭包对象。下面再看一下delegate的指定。
//==========delegate的指定===========
class TestChangeDelegateClass{
}
def testChangeDelegateClass = new TestChangeDelegateClass()
//定义一个闭包
def nestClosure = {
//在闭包中再定义一个闭包
def innerClosure = {
println "innerClosure this:" + this
println "innerClosure owner:" + owner
println "innerClosure delegate:" + delegate
}
innerClosure.delegate = testChangeDelegateClass //修改默认的delegate
innerClosure.call()
}
nestClosure.call()
/*
*输出结果
*/
//innerClosure this:variable.ClosureStudy@57cf54e1
//innerClosure owner:variable.ClosureStudy$_run_closure3@17497425
//innerClosure delegate:variable.TestChangeDelegateClass@f0da945
//可以看到delegate变为我们指定的TestChangeDelegateClass对象。this和owner是定义的时候就确定了的,无法再次修改
总结:
- this指向定义闭包处的类,在定义的时候就确定,无法再次修改
- owner在闭包定义在类与方法里的时候指向定义它的类(最近的一个),而闭包被定义在闭包中时,该闭包的owner指向定义该闭包的那个闭包对象,同样的在定义的时候就确定,无法再次修改
- delegate默认指向和woner一致,当人为的修改后,delegate指向修改后的那个对象
- 3.2、闭包的委托策略(this,owner,delegate的作用)
class Student {
def name
def self_introduction = { "My name is $name" }
@Override
String toString() {
return self_introduction.call()
}
}
class Teacher {
def name
}
//在初始化的时候传入值(先知道可以这样传,在Groovy面向对象中会讲解)
def stu = new Student(name: 'groovy')
def tea = new Teacher(name: 'java')
println stu.toString() //输出My name is groovy
//上面的输出结果毫无疑问,那么有没有什么办法不修改学生的name的情况下让输出的name不是学生的,而是老师的name呢?
//首先闭包self_introduction中的name肯定是指向定义它的类student的name,前面说过闭包的delegate是可以修改的,我们修改闭包self_introduction的delegate指向Teacher,会怎么样呢?
//修改之后再打印
stu.self_introduction.delegate = tea
println stu.toString() //My name is groovy
//发现并没有起作用,还是输出了学生的名字。这个时候就需要闭包的委托策略了
//指定闭包的委托策略为Closure.DELEGATE_FIRST
stu.self_introduction.resolveStrategy = Closure.DELEGATE_FIRST
println stu.toString() //My name is java
//每个闭包都有自己的委托策略,默认是Closure.OWNER_FIRST,表明闭包中的变量或是方法都是先从闭包指向的owner中寻找
//定义一个teacher2,属性为name1
class Teacher2 {
def name1
}
def tea2 = new Teacher2(name1: 'java')
//修改委托策略
stu.self_introduction.resolveStrategy = Closure.DELEGATE_FIRST
//指定delegate为tea2
stu.self_introduction.delegate = tea2
println stu.toString() //My name is groovy
//此时由于在tea2中没有找到name属性,所以又会从owner中寻找,因此输出的是groovy
//四种委托策略:Closure.DELEGATE_FIRST,Closure.OWNER_FIRST,另外还有Closure.DELEGATE_ONLY,Closure.OWNER_ONLY,从名字也可猜出各自的作用。
网友评论