首先看一段Java代码:
// PrintIndependenceDay.java
import java.util.Calendar;
import java.util.Date;
public class PrintIndependenceDay {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.MONTH, Calendar.JULY);
calendar.set(Calendar.DATE, 4);
calendar.set(Calendar.YEAR, 1776);
Date time = calendar.getTime();
System.out.println(time);
}
}
这段代码中我们获得Calendar的实例,然后反复的调用它的方法。是否觉得冗余,因为我们的上下文环境是已知的,在Groovy中我们利用“with”来简化,上面的代码等价的Groovy代码如下:
// PrintIndependenceDay.groovy def calendar = Calendar.instance calendar.with { clear() set MONTH, JULY set DATE, 4 set YEAR, 1776 println time }
是不是很简洁,我们还有更炫的功能,这要得益于delegate机制,看下面的例子:
// define a closure def myClosure = { // call a method that does not exist append 'Jeff' append ' was here.' } // assign a delegate to the closure def sb = new StringBuffer() myClosure.delegate = sb // execute the closure myClosure() assert 'Jeff was here.' == sb.toString()
通过动态机制,我们把闭包操作委托给了StringBuffer,我们要注意的是传递给闭包的只是StringBuffer的一个clone 。
同时我们还应该注意另外一点,首先看下面这个例子:
class ResolutionTest { def append(arg) { println "you called the append method and passed ${arg}" } def doIt() { def myclosure = { append 'Jeff was here.' } def buffer = new StringBuffer() myclosure.delegate = buffer // the append method in this ResolutionTest // will be called because resolveStrategy is // OWNER_FIRST (the default) myclosure() println '---------------------------------' // give the delegate first crack at method // calls made inside the closure myclosure.resolveStrategy = Closure.DELEGATE_FIRST // the append method on buffer will // be called because the delegate gets // first crack at the call to append() println myclosure() } static void main(String[] a) { new ResolutionTest().doIt() } }
输出结果:
you called the append method and passed Jeff was here. --------------------------------- Jeff was here.
这里我们要注意闭包一个高级特性:闭包内部有一个策略来决定何时把调用发送给委托者,每一个Groovy的闭包有一个与之关联的属性:resolveStrategy。它有四个常量取值:OWNER_FIRST, DELEGATE_FIRST, OWNER_ONLY and DELEGATE_ONLY(都定义在groovy.lang.Closure中,默认取值为:OWNER_FIRST) 。
Groovy "with"的问题:
首先看一段测试代码:
class Foo { def add(x) { println "wrong add: $x" } def doit(x) { def n = [1,2,3] n.with { add(x) each { println it } } n.add("after") println n } def test() { doit("before") } } new Foo().test()
打印结果为:
wrong add: before Foo$_doit_closure1@1979eb [1, 2, 3, "after"]
从闭包的解析策略,我们看到,默认的策略为OWNER_FIRST,这样的话,相当于我们with中的所有调用都会委托给owner,这样的问题是,如果我们将来在闭包外增加了一个同名的方法,那么with中的调用将不会按照预期进行,好的解决方法是在with中手动指定策略为DELEGATE_ONLY。
从上面的分析我们不难看懂最后的这个例子,因为each操作是Objec的方法,他其实调用的是Object的方法,这样我们的with中就不能调用所有Object的方法!