前言
这一篇我们将讲解Groovy
闭包,主要从三个层次讲解
Groovy
闭包基础详解Groovy
闭包使用详解Groovy
闭包进阶详解
1.闭包基础详解
闭包概念
就是一段代码块,和我们的方法类似,用{}包裹起来
我们看下例子
闭包参数
使用->
来区分我们的参数和闭包体.->
前面的是参数.后面的是闭包体
举个例子
Groovy
的闭包中有一个默认参数it
闭包返回值
我们回忆下方法的返回值.有两种:一种是有返回值的,一种是没有返回值的.但是在我们闭包中稍微有点不同,我们的闭包是一定有返回值的,接下来我们写个例子验证下:
- 第一种,闭包体有返回值的
- 第二种,闭包体没有返回值的
这里这个null
也是返回值的一种,这就说明在闭包中是一定会有返回值的
2.闭包使用详解
闭包进阶详解
与基本类型的结合使用
我们呢先求一个整数的阶乘,如果我们用Java
来实现的话,需要循环调用,但是在Groovy
中就可以通过闭包来实现,我们写个例子
//求指定number的阶乘
int fab(int number) {
//结果
int result = 1
1.upto(number, { num -> result *= num })
return result
}
这里我们用的是upto
这个方法,这个方法有两个参数,第一个是指定到多少,第二个就是闭包,表示每次增加要做什么
我们这个写法就是说从1
一直到我们定义的这个number
,每次都让我们这个result
乘以我们的number
我们让number
等于5,也就是5的阶乘,结果为120,我们运行下我们这段代码,看看输出的结果是不是和我们想的一样
我们知道其实求阶乘一定会用到循环的,所以我们看下upto
的源码
public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
int self1 = self.intValue();
int to1 = to.intValue();
if (self1 > to1) {
throw new GroovyRuntimeException("The argument (" + to + ") to upto() cannot be less than the value (" + self + ") it's called on.");
} else {
for(int i = self1; i <= to1; ++i) {
closure.call(i);
}
}
}
我们看到其实这个方法内部也是调用的循环
那除了upto
方法可以实现阶乘,还有没有别的方法呢?答案是有的,与之对应的还有一个方法叫downTo
,我们接下来用downTo
方法实现下
int fab2(int number) {
//结果
int result = 1
number.downto(1) {
num -> result *= num
}
return result
}
写法基本上和upto
没啥区别,upto
是从小到大,downto
是从大到小
我们运行下这段代码
结果是一样的
同样我们也贴下源码
public static void downto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
int self1 = self.intValue();
int to1 = to.intValue();
if (self1 < to1) {
throw new GroovyRuntimeException("The argument (" + to + ") to downto() cannot be greater than the value (" + self + ") it's called on.");
} else {
for(int i = self1; i >= to1; --i) {
closure.call(i);
}
}
}
除了上面2中常用的以外,我们一般还会用到times
这个方法,这里我们想求从0到100的求和,我们就可以用这个方法实现
// 从0循环到number
int cal(int number) {
int result = 0
number.times {
// times(Closure)只接收一个闭包,把闭包写到括号外,括号可以省略
num -> result += num
}
return result
}
我们看下结果
这里有个地方要注意下,就是这个times
方法是不包括你定义的这个值的,就是说比如我们要算0到100的和,我们就需要传101,我们看下源码就可以了
public static void times(Number self, @ClosureParams(value = SimpleType.class,options = {"int"}) Closure closure) {
int i = 0;
for(int size = self.intValue(); i < size; ++i) {
closure.call(i);
if (closure.getDirective() == 1) {
break;
}
}
}
我们看见源码中是小于号,没有等于,同时我们也发现这个times
方法是从0开始的,所以它不能用于求上面我们说的阶乘
与String结合使用包参数
字符串的遍历
这个主要就是调用each
方法
String str = 'This is Greathfs'
// each:遍历每个字符
str.each {
String temp -> print temp
}
我们运行下
这个each
方法就是我们调用者的本身,我们下面验证下,传一个空的闭包进去看看
查找符合条件的第一个
这个主要就是调用find
方法,find
有很多重载方法,我们主要就是调用下参数是闭包的
查找符合条件所有
这里呢调用的是findAll
方法
遍历每个字符,只要满足条件就返回true
遍历每个字符,所有都要满足条件才会返回true
遍历每个字符,经过闭包处理后,添加进list中返回
OK,我们回头看下思维导图,剩下的与数据结构结合使用和与文件等结合使用我们将在后续讲解完数据结构和文件时在讲解
3.Groovy
闭包进阶详解
3.1闭包关键变量
闭包关键变量(this、owner、delegate)
我们看见输出日志这里this
,owner
,delegate
,的值是一样的,既然一样,那为什么要定义三个变量呢?
- this:这个关键字在我们
Java
表示当前类的方法或者变量,同样在Groovy
中这个this
就表示闭包定义处的类 - owner:代表闭包定义处的类或者对象
- delegate:代表任意对象,默认与owner一致
接下来我们写一个稍微复杂点的例子,我们定义一个内部类,然后在里面定义闭包和方法,方法里面在定义闭包,同时我们打印三者
我们运行打印下结果
我们看见结果完全一样,并且指向的是Person
的实例对象而不是Groovy闭包进阶
,也就是说this
,owner
,delegate
永远指向最近的类
接下来我们在闭包中定义一个闭包,我们在看下结果如何
这里我们就发现这三者并不是完全相同的了,this
指向的是我们外面的Groovy闭包进阶
这个类实例对象,忽略nestCliser
,而owner
和delegate
他俩都指向的是我们的内部innerCloser
对象,并且与this
不相同
我们之前说过delegate
默认是与owner
一样,那我们修改下delegate
是不是就和owner
不一样了呢?我们写段代码验证下
这里我们就发现三者完全不相同了
我们总结下:
- 在大多数据情况下,
this
、owner
、delegate
的值是一样的,delegate
默认与owner
相同 - 在闭包中定义闭包时,
this
与owner
和delegate
的值是不一样的。(this指的是闭包定义处的类对象,owner指的是闭包定义处类中的闭包对象) - 在手动修改了闭包
delegate
时,owner
与delegate
的值才会不一样
3.2闭包委托策略
闭包委托策略(Closure.OWNER_FIRST、Closure.OWNER_ONLY、Closure.DELEGATE_FIRST、Closure.DELEGATE_ONLY)
这里我们以老师和学生为例说下
我们首先打印下学生的toString
方法
学生的toString
方法打印的是学生里面的name
,我们如果想改变这个值,让学生的toString
打印出来是老师的名字应该怎么弄呢?我们第一想法是修改构造方法内里内容不就行了,这里我们通过另外一种办法实现它,我们之前说过delegate
这个变量,如果我们把pretty
的delegate
指向我们的老师是不是就可以呢?我们验证下
我们看下打印结果
咦,好像并没有生效,但是理论上应该生效才对啊,哪里出了问题呢?因为我们还没有给它指定委托策略,每个闭包都有自己的委托策略,默认是OWNER_FIRST
,也就是先从owner
指向的对象寻找,我们这里owner
指向的是Student
这个类,所以我们改下这个委托策略,改成从delegate
指向的对象寻找
这样就对了
假如我们老师类中没有name
怎么办,我们把老师类中的变量name
改成name1
,我们输出看看下结果
我们看见输出又会变成学生中的XiaoMing
,因为我们制定的委托策略是Closure.DELEGATE_FIRST
,优先从delegate
中寻找,找不到就会继续从owner
中寻找 ,那如果我们把委托策略改成Closure.DELEGATE_ONLY
会变成什么样呢?我们打印下
直接报错了,因为在我们的老师中没有name
这个变量