您必须将key(String str, Closure cls) 方法中cls 的委托解析策略更改为:
cls.resolveStrategy = Closure.DELEGATE_FIRST
默认策略是Closure.OWNER_FIRST。对于 Groovy 脚本,这意味着 the owner of this closure 是由 Groovy 生成以运行脚本的类的实例。对于您的 Groovy 脚本,该类如下所示:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class my_groovy_script extends Script {
public my_groovy_script() {
CallSite[] var1 = $getCallSiteArray();
}
public my_groovy_script(Binding context) {
CallSite[] var2 = $getCallSiteArray();
super(context);
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].call(InvokerHelper.class, my_groovy_script.class, args);
}
public Object run() {
CallSite[] var1 = $getCallSiteArray();
class _run_closure1 extends Closure implements GeneratedClosure {
public _run_closure1(Object _thisObject) {
CallSite[] var3 = $getCallSiteArray();
super(my_groovy_script.this, _thisObject);
}
public Object doCall(Object it) {
CallSite[] var2 = $getCallSiteArray();
class _closure2 extends Closure implements GeneratedClosure {
public _closure2(Object _thisObject) {
CallSite[] var3 = $getCallSiteArray();
super(_run_closure1.this, _thisObject);
}
public Object doCall(Object it) {
CallSite[] var2 = $getCallSiteArray();
Object var3 = var2[0].call(var2[1].callGroovyObjectGetProperty(this));
ScriptBytecodeAdapter.setGroovyObjectProperty(var3, _closure2.class, this, (String)"a");
return var2[2].callCurrent(this, var2[3].callGroovyObjectGetProperty(this));
}
public Object doCall() {
CallSite[] var1 = $getCallSiteArray();
return this.doCall((Object)null);
}
}
return var2[0].callCurrent(this, "got", new _closure2(this.getThisObject()));
}
public Object doCall() {
CallSite[] var1 = $getCallSiteArray();
return this.doCall((Object)null);
}
}
return var1[1].callCurrent(this, "got", new _run_closure1(this));
}
public Object method(String a, Closure c) {
CallSite[] var3 = $getCallSiteArray();
Query q = (Query)ScriptBytecodeAdapter.castToType(var3[2].callConstructor(Query.class), Query.class);
ScriptBytecodeAdapter.setGroovyObjectProperty(a, my_groovy_script.class, q, (String)"a");
ScriptBytecodeAdapter.setGroovyObjectProperty(q, my_groovy_script.class, c, (String)"delegate");
var3[3].call(c);
Object str = var3[4].callGroovyObjectGetProperty(q);
return str;
}
}
正如您所见,每个 Groovy 脚本实际上都是一个扩展 groovy.lang.Script 类的类。这个类有一件重要的事情 - 它覆盖:
如果您查看这两种方法的源代码,您会发现它使用binding 对象来存储和访问闭包范围内的所有变量。这就是为什么传递给Query.key(String str, Closure cls) 的闭包不会修改Pass 类的a 字段,而是创建一个具有值GOT 的本地绑定a。您可以通过将 Closure 的解析策略更改为 Closure.DELEGATE_FIRST 来更改此行为。这可以解决问题,因为您将cls.delegate 显式设置为p 实例,因此闭包将首先在p 实例中查找字段a。希望对你有帮助。
更新了 Groovy 脚本
def method(String a, Closure c) {
Query q = new Query()
q.a = a
c.delegate = q
c.call()
def str = q.str
}
class Query {
def str
def a
void key(String str, Closure cls) {
this.str = str
Pass p = new Pass()
p.a = a
cls.delegate = p
cls.resolveStrategy = Closure.DELEGATE_FIRST
cls.call()
def val = p.a // Expcted to receive that change
println val
}
class Pass {
String a
}
}
method("got") {
key("got") {
a = a.toUpperCase() // Format Changed here
println a
}
输出
GOT
GOT