在Groovy中修改闭包中的脚本变量
我试图从函数中的闭包内部修改脚本变量。问题可以归结为:在Groovy中修改闭包中的脚本变量,groovy,Groovy,我试图从函数中的闭包内部修改脚本变量。问题可以归结为: @groovy.transform.Field int myField = 0 incrementField() assert myField == 1 def incrementField() { 1.times { myField++ } } 我认为问题与此有关,但我不能完全理解文档。这种行为是由类引起的,它覆盖了以下方法: 示例中显示的闭包使用delegate设置为脚本对象,这就是为什么当您尝试访问或修改脚本中定
@groovy.transform.Field int myField = 0
incrementField()
assert myField == 1
def incrementField() {
1.times { myField++ }
}
我认为问题与此有关,但我不能完全理解文档。这种行为是由类引起的,它覆盖了以下方法:
delegate
设置为脚本对象,这就是为什么当您尝试访问或修改脚本中定义的字段时,会执行两个重写的方法
现在让我们看看当您的示例结束时会发生什么
{ myField++ }
首先,调用getProperty(“myField”)
以返回与此属性关联的值。此方法的实现方式如下:
public Object getProperty(String property) {
try {
return binding.getVariable(property);
} catch (MissingPropertyException e) {
return super.getProperty(property);
}
}
资料来源:
binding
对象在开头只包含一个变量-闭包的args
数组。如果我们看看binding.getVariable(property)
方法的实现,我们将看到:
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;
}
资料来源:
在本例中,将抛出MissingPropertyException
,因此Script.getProperty(property)
方法返回Groovy脚本中定义的fieldmyField
值。然后Groovy将该值递增1,并尝试将该新值设置为字段myField
。在这种情况下,Script.setProperty(property,value)
被调用:
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);
}
资料来源:
正如您所看到的,它使用绑定
对象设置这个新值。如果我们显示binding.variables
,我们将看到现在这个内部映射包含两个条目:args->[:]
和myField->1
。它解释了为什么脚本中的断言总是失败。您定义的闭包体从未从脚本类到达myField
字段
变通办法
如果您对Script
类重写setProperty(property,value)
方法这一事实不满意,则始终可以在脚本中手动重写它,并使用与GroovyObjectSupport.setProperty(property,value)
相同的实现。只需将以下方法添加到Groovy脚本中:
@Override
void setProperty(String property, Object newValue) {
getMetaClass().setProperty(this, property, newValue)
}
现在,在
incrementField
中定义的闭包将为class字段而不是bindings
对象设置一个新值。当然,它可能会引起一些奇怪的副作用,你必须意识到这一点。我希望它能有所帮助。使用closure delegate找到了一个可能的解决方案:
@groovy.transform.Field def stuff = [
myField : 0
]
incrementField()
assert stuff.myField == 1
def incrementField() {
def body = { myField++ }
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = stuff
1.times body
}
一位同事提出了我贴出的答案,但你的答案要干净得多。非常好的解释!