下面显示的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.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
但我的预期输出是:
GOT
GOT
为什么a = a.toUpperCase()
不会在p
之后更改cls.call()
对象中的值?如何通过这个改变?
答案 0 :(得分:1)
您必须将cls
方法中key(String str, Closure 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
类的类。关于这门课有一个重要的事情 - 它取代了:
public Object getProperty(String property)
public void setProperty(String property, Object newValue)
如果您查看两种方法的源代码,您将看到它使用binding
对象来存储和访问闭包范围内的所有变量。这就是为什么传递给Query.key(String str, Closure cls)
的闭包不会修改类a
的{{1}}字段,而是创建一个值为Pass
的本地绑定a
。您可以通过将Closure的解析策略更改为GOT
来更改此行为。这样做可以解决问题,因为您明确将Closure.DELEGATE_FIRST
设置为cls.delegate
实例,因此闭包首先会在p
实例中查找字段a
。我希望它有所帮助。
p
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
}