Groovy中文文档——闭包
本文翻译自Groovy,主要介绍了Groovy语言的闭包。
.1. 语法.1.1. 定义闭包闭包按照以下语法定义:
下面是一些关于定义闭包的例子: { item++ } 1
{ -> item++ } 2
{ println it } 3
{ it -> println it } 4
{ name -> println name } 5
{ String x,int y 6->
println "hey ${x} the value is ${y}"
}
{ reader 7->
def line = reader.readLine()
line.trim()
}
1.闭包引用一个 .1.2. 闭包实例闭包是groovy.lang.Closure类的实例,他可以赋给一个变量或者一个属性作为其他的变量。尽管他是一个代码块: def listener = { e -> println "Clicked on $e.source" } 1
assert listener instanceof Closure
Closure callback = { println 'Done!' } 2
Closure<Boolean> isTextFile = {File it -> 3
it.name.endsWith('.txt')
}
1.闭包是groovy.lang.Closure类的实例,可以被赋给一个变量 .1.3 调用闭包闭包作为一个匿名的代码块可以像其它方法那样被调用。可以像下面这样定义一个没有输入参数的闭包:
闭包内部的代码只有在被调用时才会执行,他可以被当作一个变量: def isOdd = { int i-> i%2 == 1 } 1
assert isOdd(3) == true 2
assert isOdd.call(2) == false 3
def isEven = { it%2 == 0 } 4
assert isEven(3) == false 5
assert isEven.call(2) == true 6
1.定义一个接收 与方法不同,闭包在被调用时总是返回一个结果。下一节将介绍如何声明闭包的参数,什么时候使用它们,以及什么是 .2. 参数..2.1. 普通参数闭包的参数遵循与普通方法参数一样的规则:
参数用逗号隔开: def closureWithOneArg = { str -> str.toUpperCase() }
assert closureWithOneArg('groovy') == 'GROOVY'
def closureWithOneArgAndExplicitType = { String str -> str.toUpperCase() }
assert closureWithOneArgAndExplicitType('groovy') == 'GROOVY'
def closureWithTwoArgs = { a,b -> a+b }
assert closureWithTwoArgs(1,2) == 3
def closureWithTwoArgsAndExplicitTypes = { int a,int b -> a+b }
assert closureWithTwoArgsAndExplicitTypes(1,2) == 3
def closureWithTwoArgsAndOptionalTypes = { a,int b -> a+b }
assert closureWithTwoArgsAndOptionalTypes(1,2) == 3
def closureWithTwoArgAndDefaultValue = { int a,int b=2 -> a+b }
assert closureWithTwoArgAndDefaultValue(1) == 3
.2.2 隐式参数当闭包没有显示的定义参数列表时( def greeting = { "Hello,$it!" }
assert greeting('Patrick') == 'Hello,Patrick!'
下面是完整的写法: def greeting = { it -> "Hello,Patrick!'
如果你想声明一个不接受参数的闭包,限制调用时不带参数,那么你必须显式声明一个空参数列表: def magicNumber = { -> 42 }
// this call will fail because the closure doesn't accept any argument
magicNumber(11)
.2.3 可变参数闭包可以像其它方法那样声明一个可变的参数列表。当参数列表的最后一个参数是长度可变的(或者是一个数组),那么这个闭包就能接收数量不定的参数: def concat1 = { String... args -> args.join('') } 1
assert concat1('abc','def') == 'abcdef' 2
def concat2 = { String[] args -> args.join('') } 3
assert concat2('abc','def') == 'abcdef'
def multiConcat = { int n,String... args -> 4
args.join('')*n
}
assert multiConcat(2,'abc','def') == 'abcdefabcdef'
1.闭包可以接收数量可变的String参数 .3. 委托用法 (Delegation strategy).3.1. Groovy闭包 VS lambda表达式Groovy定义闭包(作为Closure类的实例),与Java8的lambda有很大的不同。 .3.2 this,owner,delegate要理解delegate的概念,我们首先要说明this在闭包内的含义。闭包实际上定义了三个截然不同的变量(当然
.3.2.1. this在一个闭包内调用 class Enclosing { void run() { def whatIsThisObject = { getThisObject() } 1 assert whatIsThisObject() == this 2 def whatIsThis = { this } 3 assert whatIsThis() == this 4 } } class EnclosedInInnerClass { class Inner { Closure cl = { this } 5 } void run() { def inner = new Inner() assert inner.cl() == inner 6 } } class NestedClosures { void run() { def nestedClosures = { def cl = { this } 7 cl() } assert nestedClosures() == this 8 } }
1.一个闭包被定义 当然也可以在闭包内通过this调用类的方法: class Person {
String name
int age
String toString() { "$name is $age years old" }
String dump() {
def cl = {
String msg = this.toString() 1
println msg
msg
}
cl()
}
}
def p = new Person(name:'Janice',age:74)
assert p.dump() == 'Janice is 74 years old'
1.闭包通过 .3.2.2. owner闭包内的 他将返回定义此闭包的对象,不管他是类或者闭包: class Enclosing {
void run() {
def whatIsOwnerMethod = { getOwner() } 1
assert whatIsOwnerMethod() == this 2
def whatIsOwner = { owner } 3
assert whatIsOwner() == this 4
}
}
class EnclosedInInnerClass {
class Inner {
Closure cl = { owner } 5
}
void run() {
def inner = new Inner()
assert inner.cl() == inner 6
}
}
class NestedClosures {
void run() {
def nestedClosures = {
def cl = { owner } 7
cl()
}
assert nestedClosures() == nestedClosures 8
}
}
1.一个闭包被定义在 .3.2.3. delegate闭包的delegate可以通过使用delegate或者调用 class Enclosing { void run() { def cl = { getDelegate() } 1 def cl2 = { delegate } 2 assert cl() == cl2() 3 assert cl() == this 4 def enclosed = { { -> delegate }.call() 5 } assert enclosed() == enclosed 6 } }
1.通过闭包调用 闭包的 class Person {
String name
}
class Thing {
String name
}
def p = new Person(name: 'Norman')
def t = new Thing(name: 'Teapot')
然后定义一个返回 然后通过改变闭包的 upperCasedName.delegate = p
assert upperCasedName() == 'NORMAN'
upperCasedName.delegate = t
assert upperCasedName() == 'TEAPOT'
一定意义上说,在闭包内部使用一个自定义的变量(target)也能有同样的效果: def target = p
def upperCasedNameUsingVar = { target.name.toUpperCase() }
assert upperCasedNameUsingVar() == 'NORMAN'
然而,他们有很大的不同点:
.3.2.4. Delegation 策略( strategy)在闭包内,每当我们没有显示指定对象的去访问一个属性,那么delegate 策略是involved: String name
}
def p = new Person(name:'Igor')
def cl = { name.toUpperCase() } 1
cl.delegate = p 2
assert cl() == 'IGOR' 3
1.在闭包内,属性 这个代码成功的原因是闭包内的
让我们通过下面的代码了解下默认的 class Person {
String name
def pretty = { "My name is $name" } 1
String toString() {
pretty()
}
}
class Thing {
String name 2
}
def p = new Person(name: 'Sarah')
def t = new Thing(name: 'Teapot')
assert p.toString() == 'My name is Sarah' 3
p.pretty.delegate = t 4
assert p.toString() == 'My name is Sarah' 5
1.在这个例子中,我们定义了一个闭包,在闭包内引用了一个变量 然而,我们可以更改闭包的选择策略: p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
assert p.toString() == 'My name is Teapot'
通过改变 class Person {
String name
int age
def fetchAge = { age }
}
class Thing {
String name
}
def p = new Person(name:'Jessica',age:42)
def t = new Thing(name:'Printer')
def cl = p.fetchAge
cl.delegate = p
assert cl() == 42
cl.delegate = t
assert cl() == 42
cl.resolveStrategy = Closure.DELEGATE_ONLY
cl.delegate = p
assert cl() == 42
cl.delegate = t
try {
cl()
assert false
} catch (MissingPropertyException ex) {
// "age" is not defined on the delegate
}
在这个例子里,我们定义了两个类: .4. 在GString中使用闭包看下面的代码: def x = 1
def gs = "x = ${x}"
assert gs == 'x = 1'
代码将会按照我们所预期的那样运行,但是如果增加下面的代码,将会发生什么呢: x = 2
assert gs == 'x = 2'
你将看到运行失败!有两个原因:
def x = 1
def gs = "x = ${-> x}"
assert gs == 'x = 1'
x = 2
assert gs == 'x = 2'
让我们看一下这变化的代码有什么不一样: class Person {
String name
String toString() { name } 1
}
def sam = new Person(name:'Sam') 2
def lucy = new Person(name:'Lucy') 3
def p = sam 4
def gs = "Name: ${p}" 5
assert gs == 'Name: Sam' 6
p = lucy 7
assert gs == 'Name: Sam' 8
sam.name = 'Lucy' 9
assert gs == 'Name: Lucy' 10
1.
so,如果你不想依赖改变对象或者包装对象,你就必须在GString中使用闭包,通过声明一个空的参数列表: class Person { String name String toString() { name } } def sam = new Person(name:'Sam') def lucy = new Person(name:'Lucy') def p = sam // Create a GString with lazy evaluation of "p" def gs = "Name: ${-> p}" assert gs == 'Name: Sam' p = lucy assert gs == 'Name: Lucy'
.5. 闭包强制?(Closure coercion)闭包可以被装换为接口或者单例-抽象方法类型。在官网有更详细的说明。 .6. 函数式编程就像lambda表达式在Java8中一样,闭包是Groovy语言函数式编程范例的核心。一些函数式编程操作功能在 .6.1 柯里化 Currying.6.2 Memoization我们可以用Memoization技术来代替函数中太多的递归调用。Memoization是一种可以缓存之前运算结果的技术,这样我们就不需要从新计算那些已经计算过的结果。 .6.3 Composition.6.4 Trampoline.6.5 方法指针把常规方法当做闭包是一个很常用的操作。例如,你可能想要使用闭包的柯里化能力,但是这在平常的方法中是不被允许的。在Groovy中,你可以通过方法指针操作把任何的方法变成闭包。 错误是难免的,如果读者发现了,还望留言指出!有什么不甚理解的地方也欢迎留言讨论 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |