【问题标题】:Groovy closure does not modify a value in delegated objectGroovy 闭包不会修改委托对象中的值
【发布时间】:2017-10-07 05:22:43
【问题描述】:

下面显示的 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()cls.call() 之后没有更改p 对象中的值?如何通过此更改?

【问题讨论】:

    标签: object groovy closures


    【解决方案1】:

    您必须将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
    

    【讨论】:

      猜你喜欢
      • 2016-05-31
      • 2018-10-29
      • 1970-01-01
      • 2019-10-08
      • 2014-06-20
      • 2013-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多