《groovy实战》读书笔记
简单来说,Groovy是在普通的常用 Java 对象上增加了一些独特的方法和快捷方式,使得它们更容易使用。具体体现在以下几个方面:
1、可以省略分号和修饰符
2、Groovy 是没有类型的 Java 代码,允许省略变量类型
3、灵活的闭包
面向对象的最高原则是对象有自己的行为和数据,闭包也是对象,他主要的目的是他们的行为。为什么说闭包是一个普通对象呢,因为你能够通过一个变量引用到它,正如你能够引用到任何别的对象一样。所以,一个闭包是被包装为一个代码块的对象,实际上闭包像一个可以接受参数并且能够有返回值的方法。
始终记住:闭包是由一些代码组 成的对象,并且 groovy 为闭包提供了简洁的语法。
一、声明闭包
闭包的语法:在一个方法调用的后面,放置闭包代码在 一对花括号里,闭包的参数和代码通过箭头(->)进行分隔。
例如:
{ line -> println line}
1、简单的声明方式
当只有一个参数传递给闭包 的时候,这个参数的声明是可选的,魔术变量 it 代替了声明
我们知道闭包就是一个对象,因此在我们Groovy集合中,很多方法可以接收一个闭包对象作为参数。
def log = ' ';
(1..10).each {counter -> log += counter }
println log
也可以写成
def log = ' ';
(1..10).each {log += it }
println log
上面的each方法其实就是传入了一个闭包类型的对象,只是将方法的括号()给省略了。在方法的调用中,如果直接传入一个闭包块,那么方法的括号需要直接省略。我们可以看看each方法的定义。
public static <T> List<T> each(List<T> self,@ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
return (List<T>) each((Iterable<T>) self,closure);
}
可以看到,each方法接收了两个对象,一个就是对象的本身,这个是默认的参数,不需要显式给出,另一个就是一个闭包对象,从这里可以看出,其实我们就是传入了一个闭包对象进去了。在执行闭包块的时候,就是将每个元素作为参数传递给这个闭包块,闭包块里面的变量就是用来接收这个变量而已,这样整个代码就很好理解了。
2、使用赋值的方式声明闭包
既然闭包是一个对象,我们当然可以定义这个对象,并且把它赋值给一个闭包类型的变量。
def printer = {line -> println line }
闭包声明在花括号中并且赋给了 printer 变量。
这个其实本质跟上面第一个定义一致,只是它单独进行了定义并且用了一个变量来进行保存。
我们可以将上面的例子就行改写
def log = ' ';
def add = {log += it }
(1..10).each(add)
println log
现在就很好理解了,确实each方法接收的确实是一个闭包对象。
所以,我们就可以把闭包当做一种特殊的类型看待就行,既然它是一种类型,它的用法跟其他的类型其实是一样的,它既可以当做参数类型,也可以当做返回类型。
def Closure getPrinter() {
return { line -> println line }
}
上面定义了一个方法,方法的返回类型就是一个Closure闭包类型。
方法有一个方法体,可选的返回值,能够接受参数,并且能够被调用,与闭包非常相似,因此 groovy 可以让你重 用方法中存在的代码,所以,闭包可以引用一个方法,也就是将一个方法块转换成一个闭包对象,引用一个方法作为闭包是使用 reference.&操作方法,reference表示方法所在的对象,操作方法就是我们需要转换为闭包的方法。
class Sample {
int limit;
Sample(int limit) {
this.limit = limit;
}
boolean validate(String value) {
return value.length() <= limit;
}
}
Sample first = new Sample(6);
Sample second = new Sample(5);
// 将first对象的方法转换为一个闭包
Closure firstClosure = first.&validate
def word = ['long string','medium','short','tiny']
// find方法需要接收的就是一个闭包对象,因此,可以将上面的闭包对象传递进去
assert 'medium' == word.find(firstClosure)
assert 'short' == word.find(second.&validate)
需要注意的是,我们上面在将方法转换为闭包对象的时候,只是通过方法名,如果一个类中有多个重写的方法,仅仅通过方法名,可能就会出现匹配多个方法的情况,因此,在闭包里面到底是将哪个方法转换为闭包对象这个是根据闭包对象的使用情况来决定对应的是哪个方法。
class Sample {
int limit;
Sample(int limit) {
this.limit = limit;
}
boolean validate(String value) {
return value.length() <= limit;
}
boolean validate(int value) {
return value <= limit;
}
}
Sample first = new Sample(6);
Closure firstClosure = first.&validate
// 会调用参数为String类型的那个方法
assert firstClosure('hello')
// 会调用参数为int类型的那个方法
assert firstClosure(5)
从这里也可以看到,它的调用情况具体是根据我们传递的参数来决定的,从这里我们也可以看到对于闭包对象我们也可以当做一个方法来进行调用。
上面演示的就是闭包只有一个参数,其实也可以有多个参数,跟方法一样,例如:
map = ['a':1,'b':2]
map.each {key,value -> map[key] = value * 2}
->前面是参数,后面是执行语句
二、调用闭包
def addr = {x,y -> return x + y}
println addr(3, 4)
println addr.call(3, 4)
上面有两种调用方法,一种是类似于方法的调用,一种是使用call方法
在调用闭包的时候,需要传递所有准确的参数给闭包,除非闭包定义了缺省值,当忽略 了某个参数的时候,这个参数的缺省值被使用
def adder = { x,y=5 -> return x+y }
println addr(3, 4)
三、Closure方法
类 groovy.lang.Closure 是一个普通的 java 类,所以它也有各种各样不同功能的调用方法
参数的数量
def caller (Closure closure)
{
closure.getParameterTypes().size()
}
过闭包的 getParameterTypes 方法可以获取闭包参数的数量
curry方法
Closure 的 curry 方法返回当前闭包的一个克隆品,这个克隆品已经绑定了一个或者多个给定的参数,参数的绑定是从左向右进行的
def addr = {x,y -> return x + y}
def addOne = addr.curry(1)
println addOne(5)
在调用 curry 方法的时候创建了一个新的闭包,它的第一个参数已经被固定为 1
四、从闭包返回结果
有2种返回结果的方式:
(1)闭包最后一个表达式执行的结果,并且这个结果被返回,这叫做结束返回(end
return),在最后的语句前面的return关键字是可选的。
(2)使用return关键字从闭包的任何地方返回。
[1,2,3].collect{ it * 2 }
[1,3].collect{ return it * 2 }
上面两个表达式执行结果相同
在闭包体外面,任何出现return的地方都会导致离开当前方法,当在闭包体内出现return 语句时,这仅仅结束闭包的当前计算,有点类似于continue,例如,当使用List.each时,从闭包 的提前返回不会引起从each方法的提前返回——闭包仍旧会在list的下一个元素出现时被调用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。