这里对Groovy闭包的基本使用操作进行介绍
声明定义
Groovy采用 { 参数列表 -> 语句 } 的形式定义一个闭包。需要注意的是,如果闭包中没有通过->显式声明参数列表, Groovy会默认提供一个名为it的缺省参数。故如果期望声明一个无参数的闭包,需要显式添加->来声明空参数列表, 以避免Groovy提供默认参数it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class ClosureDemo { static void define() { def range1 = 1..2 println("\n----------------- Test 1 -----------------") range1.each { it -> println("Test 1: $it") }
println("\n----------------- Test 2 -----------------") range1.each { println("Test 2: $it") }
println("\n----------------- Test 3 -----------------") def closure3 = { -> println("Hello World") } closure3.call()
println("\n----------------- Test 4 -----------------") def closure1 = { num -> println("Test 4: $num") } assert closure1 instanceof Closure range1.each( closure1 )
println("\n----------------- Test 5 -----------------") Closure closure2 = { println("Test 5: $it") } assert closure2 instanceof Closure range1.each( closure2 ) } }
|
测试结果如下,符合预期
事实上,关于闭包的参数类型的数量,我们还可以通过闭包的getParameterTypes()方法进一步验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class ClosureDemo { static void other() { Closure closure1 = { print("Hello: $it") } assert closure1.getParameterTypes().size() == 1
Closure closure2 = { print("Hello") } assert closure2.getParameterTypes().size() == 1
Closure closure3 = { -> print("Hello") } assert closure3.getParameterTypes().size() == 0
Closure closure4 = { x,y,z -> print("Hello") } assert closure4.getParameterTypes().size() == 3 } }
|
调用
由于Groovy本身就是一个对象实例,故自然是可以直接调用的。具体地,可通过添加括号、call方法进行传参、调用。同时闭包在定义过程中还支持带有缺省值的参数。这样在进行闭包调用时,对于未给定的参数值, 闭包会使用缺省值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class ClosureDemo {
static void call() { def closure1 = { "123" } assert closure1() == "123" assert closure1.call() == "123"
def closure2 = { x, y -> x+y } assert closure2(1,3) == 4
def closure3 = { String x, String y -> "${x}~~${y}" } assert closure3.call('2','7') == "2~~7"
def closure4 = { x, y=5 -> x*y } assert closure4.call( 4,7 ) == 28 assert closure4.call( 4) == 20 } }
|
返回值
对于闭包而言,闭包中如果未显式使用return语句, 返回最后一个表达式的结果;反之如果闭包中显式使用return语句, 返回指定值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class ClosureDemo { static void returnResult() { def closure1 = { -> def list = [] list.add("Bob") list.add("Aaron") } def result1 = closure1.call() assert result1 == true
def closure2 = { -> def list = [] list.add("Bob") list.add("Aaron") return list } def result2 = closure2.call() assert result2 == ["Bob", "Aaron"] } }
|
方法闭包
事实上我们还可以通过 .&方法指针运算符 获取方法的引用作为闭包,即所谓的方法闭包。与此同时,方法闭包支持重载,Groovy在运行时会选择合适的方法版本进行执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| class ClosureDemo { static void methodClosure() { def list = ["Tony", "Amy", "civilization", "Aaron"]
StrRule strRule1 = new StrRule(4) MethodClosure closure1 = strRule1.&validate assert closure1 instanceof Closure def list1 = list.findAll( closure1 ) assert list1 == ["Tony", "Amy"]
StrRule strRule2 = new StrRule(6) def list2 = list.findAll( strRule2.&validate ) assert list2 == ["Tony", "Amy", "Aaron"]
Handler handler = new Handler() def closure2 = handler.&handle def result1 = closure2.call("Aaron") assert result1 == "AARON" def result2 = closure2.call(12) assert result2 == 24 def result3 = closure2.call(3, 7) assert result3 == 10 } }
class StrRule{ int limit
StrRule(int limit) { this.limit = limit }
boolean validate(String str) { return str.size() <= limit } }
class Handler{ String handle(String str) { return str.toUpperCase() }
int handle(int num) { return 2 * num }
int handle(int x, int y) { return x + y } }
|
闭包三大属性:this、owner、delegate属性
Groovy的闭包中的三大属性:this、owner、delegate属性
闭包的this属性
闭包中的this指的是闭包定义时所处的类的实例。具体地:
- 闭包定义在类中:闭包中的this 指的是 闭包定义处所在类的相应实例
- 闭包定义在内部类中:闭包中的this 指的是 闭包定义处所在内部类的相应实例
- 闭包A定义在另一个闭包B中:则闭包A中的this 指的依然是 闭包A所在类的相应实例
总而言之,this指的是距离闭包定义处最近类相应的实例。测试代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| class ThisInClosure { static void main(String[] args) { Employee employee = new Employee("Aaron", 18) assert employee.getInfo() == "[Employee Info]: <name> : Aaron, <age>: 18" assert employee.getInfo2() == "[Employee Info]: <name> : Aaron, <age>: 18"
Closure closure1 = employee.test1() assert closure1.call() == employee
Closure closure2 = employee.test2() assert closure2.call() == employee
Employee.Inner inner = employee.getInnerInstance() Closure closure3 = inner.test3() assert closure3.call() != employee assert closure3.call() == inner
Closure closure4 = employee.test4() assert closure4.call() == employee } }
class Employee { String name
int age
Employee(String name, int age) { this.name = name this.age = age }
String getInfo() { return "[Employee Info]: <name> : $name, <age>: $age" }
String getInfo2() { return this.getInfo() }
def test1() { def closure = { this } return closure }
def test2() { def closure = { getThisObject() } return closure }
def test4() { def closure = { -> def nestedClosure = { -> this } def result = nestedClosure.call() return result } return closure }
def getInnerInstance() { return new Inner() }
class Inner{ def test3() { def closure = { this } return closure } } }
|
闭包的owner属性
闭包中的owner指的是闭包定义时所处的封闭对象的实例。这里的封闭对象可以是一个类,也可以是一个闭包。具体地:
- 闭包定义在类中:闭包中的owner 指的是 闭包定义处所在类的相应实例
- 闭包定义在内部类中:闭包中的owner 指的是 闭包定义处所在内部类的相应实例
- 闭包A定义在另一个闭包B中:则闭包A中的owner 指的是 闭包A所在闭包B的相应实例
总而言之,owner指的是距离闭包定义处最近封闭对象相应的实例。测试代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| class OwnerInClosure { static void main(String[] args){ Person person = new Person("Bob", 25)
Closure closure1 = person.test1() assert closure1.call() == person
Closure closure2 = person.test2() assert closure2.call() == person
Person.Inner inner = person.getInnerInstance() Closure closure3 = inner.test3() assert closure3.call() != person assert closure3.call() == inner
Closure closure4 = person.test4() assert closure4.call() != person assert closure4.call() == closure4 } }
class Person { String name
int age
Person(String name, int age) { this.name = name this.age = age }
def test1() { def closure = { owner } return closure }
def test2() { def closure = { getOwner() } return closure }
def test4() { def closure = { -> def nestedClosure = { -> owner } def result = nestedClosure.call() return result }
assert closure() == closure
return closure }
def getInnerInstance() { return new Inner() }
class Inner{ def test3() { def closure = { owner } return closure } } }
|
闭包的delegate属性
上面介绍闭包中的this、owner,其所指向的对象都是确定的。即开发者不可以进行修改,使其指向别的对象。为此闭包提供了一个delegate。 默认情况下, 闭包的 delegate 被设置为 owner。故当一个闭包被定义在类、内部类、另一个闭包中时,delegate所指向的对象与owner的行为完全一致。测试代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class DelegateInClosure { static void basic(){ Staff staff = new Staff("Amy", 38)
Closure closure1 = staff.test1() assert closure1.call() == staff
Closure closure2 = staff.test2() assert closure2.call() == staff } }
class Staff { String name
int age
Staff(String name, int age) { this.name = name this.age = age }
def test1() { def closure = { delegate } return closure }
def test2() { def closure = { getDelegate() } return closure } }
|
由于闭包的delegate属性可以进行修改赋值。这就为我们开发过程提供了很多便利。示例代码如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class DelegateInClosure { static void modifyDelegate() { Staff staff1 = new Staff("Tony", 22) Animal animal1 = new Animal("Tom", "Cat")
def closure = { -> def nameStr = delegate.name nameStr.toUpperCase() }
assert closure.delegate == DelegateInClosure.class
closure.delegate = staff1 assert closure.call() == "TONY"
closure.delegate = animal1 assert closure.call() == "TOM"
def temp = staff1 def closure2 = { temp.name.toUpperCase() } assert closure2.call() == "TONY" temp = animal1 assert closure2() == "TOM" } }
class Animal{ String name
String type
Animal(String name, String type) { this.name = name this.type = type } }
|
参考文献
- Groovy In Action · 2nd Edition Dierk König、Guillaume Laforge著