関数のクロージャ内からスクリプト変数を変更しようとしています。問題はこれに要約することができます:
@groovy.transform.Field int myField = 0
incrementField()
assert myField == 1
def incrementField() {
1.times { myField++ }
}
この問題はクロージャーデリゲートと関係があると思いますが、ドキュメントに頭を悩ませることはできません。
この動作は、groovy.lang.Script
クラスと、次のメソッドをオーバーライドするという事実が原因で発生します。
例で示したクロージャはdelegate
、スクリプトオブジェクトに設定されたものを使用します。そのため、スクリプトで定義されたフィールドにアクセスまたは変更しようとすると、オーバーライドされた両方のメソッドが実行されます。
次に、例が終了したときに何が起こるかを見てみましょう。
{ myField++ }
まず、getProperty("myField")
このプロパティに関連付けられた値を返すために呼び出されます。このメソッドは次のように実装されます。
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
配列という1つの変数しか含まれていません。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;
}
ソース:https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Binding.java#L56
この場合MissingPropertyException
はスローされているため、Script.getProperty(property)
メソッドはmyField
Groovyスクリプトで定義されたフィールドの値を返します- 0
。次に、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);
}
ソース:https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Script.java#L62
ご覧のとおり、bindings
オブジェクトを使用してこの新しい値を設定します。表示binding.variables
すると、この内部マップに2つのエントリが含まれていることがわかります: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
は、bindings
オブジェクトではなくクラスフィールドに新しい値を設定します。もちろん、それはいくつかの奇妙な副作用を引き起こす可能性があります、あなたはそれを知っている必要があります。お役に立てば幸いです。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加