从Groovy中的Closure修改脚本变量 [英] modify script variable from a Closure in Groovy
问题描述
我正在尝试从函数的闭包内部修改脚本变量.问题可以归结为:
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屋!