Groovy闭包不会修改委托对象中的值

时间:2017-10-07 05:22:43

标签: object groovy closures

下面显示的G​​roovy代码包含闭包和方法。该格式更改了标签,并期望接收该更改标签显示问题和要求。

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()对象中的值?如何通过这个改变?

1 个答案:

答案 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类的类。关于这门课有一个重要的事情 - 它取代了:

如果您查看两种方法的源代码,您将看到它使用binding对象来存储和访问闭包范围内的所有变量。这就是为什么传递给Query.key(String str, Closure cls)的闭包不会修改类a的{​​{1}}字段,而是创建一个值为Pass的本地绑定a。您可以通过将Closure的解析策略更改为GOT来更改此行为。这样做可以解决问题,因为您明确将Closure.DELEGATE_FIRST设置为cls.delegate实例,因此闭包首先会在p实例中查找字段a。我希望它有所帮助。

更新了Groovy脚本

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

    }