从Groovy中的Closure修改脚本变量 [英] modify script variable from a Closure in Groovy

查看:199
本文介绍了从Groovy中的Closure修改脚本变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从函数的闭包内部修改脚本变量.问题可以归结为:

I am trying to modify a script variable from inside a closure in a function. The problem can be distilled down to this:

@groovy.transform.Field int myField = 0

incrementField()
assert myField == 1

def incrementField() {
    1.times { myField++ }
}

我认为问题与封闭代理有关,但我不能完全把我的头缠在文档上.

I think the problem has something to do with closure delegates, but I cannot quite wrap my head around the docs.

推荐答案

此行为是由

This behavior is caused by groovy.lang.Script class and the fact that it overrides following methods:

在示例中显示的闭包使用设置为脚本对象的delegate,这就是为什么当您尝试访问或修改脚本中定义的字段时两个覆盖的方法都被执行的原因.

Closure you have shown in the example uses delegate set to a script object and that's why both overridden methods get executed when you try to access or modify field defined in a script.

现在让我们看看当示例结束时会发生什么

Now let's see what happens when your example reaches closure

{ myField++ }

首先,调用getProperty("myField")以返回与此属性关联的值.此方法的实现方式为:

Firstly, getProperty("myField") is called to return a value associated with this property. This method is implemented as:

public Object getProperty(String property) {
    try {
        return binding.getVariable(property);
    } catch (MissingPropertyException e) {
        return super.getProperty(property);
    }
}

来源: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Script.java#L54

binding对象的开头仅包含一个变量-闭包的args数组.如果我们看一下binding.getVariable(property)方法的实现,我们将看到:

binding object contains only one variable in the beginning - closure's args array. If we take a look at implementation of binding.getVariable(property) method we will see:

public Object getVariable(String name) {
    if (variables == null)
        throw new MissingPropertyException(name, this.getClass());

    Object result = variables.get(name);

    if (result == null && !variables.containsKey(name)) {
        throw new MissingPropertyException(name, this.getClass());
    }

    return result;
}

来源: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Binding.java#L56

在我们抛出MissingPropertyException的情况下,因此Script.getProperty(property)方法返回在Groovy脚本-0中定义的字段myField的值.然后,Groovy将该值增加1,并尝试将此新值设置为字段myField.在这种情况下,Script.setProperty(property, value)被调用:

In our case MissingPropertyException is being thrown, so Script.getProperty(property) method returns a value of field myField defined in our Groovy script - 0. Then Groovy increments this value by 1 and tries to set this new value to a field myField. In this case Script.setProperty(property, value) is being called:

public void setProperty(String property, Object newValue) {
    if ("binding".equals(property))
        setBinding((Binding) newValue);
    else if("metaClass".equals(property))
        setMetaClass((MetaClass)newValue);
    else
        binding.setVariable(property, newValue);
}

来源: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Script.java#L62

如您所见,它使用bindings对象设置了这个新值.如果显示binding.variables,我们将看到现在此内部映射包含两个条目:args -> [:]myField -> 1.它解释了为什么脚本中的断言总是失败.您定义的闭包主体永远不会到达脚本类的myField字段.

As you can see it sets this new value using bindings object. If we display binding.variables we will see that now this internal map contains two entries: args -> [:] and myField -> 1. It explains why assertion in your script always fails. Body of the closure you have defined never reaches myField field from the script class.

如果您对Script类覆盖setProperty(property, value)方法的事实不满意,则始终可以在脚本中手动覆盖它,并使用与GroovyObjectSupport.setProperty(property, value)相同的实现.只需将以下方法添加到您的Groovy脚本中:

If you are not satisfied with the fact that Script class overrides setProperty(property, value) method you can always override it by hand in your script and use same implementation as GroovyObjectSupport.setProperty(property, value). Simply add below method to your Groovy script:

@Override
void setProperty(String property, Object newValue) {
    getMetaClass().setProperty(this, property, newValue)
}

incrementField中定义的闭包现在将为类字段而不是bindings对象设置新值.当然,这可能会导致一些怪异的副作用,您必须意识到这一点.希望对您有帮助.

Now closure defined in incrementField will set a new value to a class field instead of to a bindings object. Of course it may cause some weird side effects, you have to be aware of that. I hope it helps.

这篇关于从Groovy中的Closure修改脚本变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆