Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/342.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Groovy中修改闭包中的脚本变量_Groovy - Fatal编程技术网

在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脚本中定义的field
myField
值。然后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
}

一位同事提出了我贴出的答案,但你的答案要干净得多。非常好的解释!