8

Groovyのクロージャとデリゲートのあるものに出くわしましたが、それが言語の公式な部分であるか、おそらくバグであるかはわかりません。

基本的に、外部ソースから文字列として読み込んだクロージャを定義しています。クロージャを定義するクラスの変数の1つは、クロージャによって変更する必要があります。何がうまくいくか、何がうまくいかないかを示す簡単な例を書きました。

以下のテストコードを見ると、変数を定義するクラスがあります。

animal = "cat"

動物変数を変更しようとする文字列からその場で定義された2つのクロージャ。

これは機能します>

String code = "{ ->   delegate.animal = 'bear';   return name + 'xx' ; }"

しかし、これはしません

String code = "{ ->   animal = 'bear';   return name + 'xx' ; }"

変更する変数を「デリゲート」で明示的に修飾する必要があるようです。これが機能するために。(値を変更するためにクロージャーが呼び出すために、囲んでいるクラスにセッターを定義することもできると思います。)

だから....私はこれを機能させる方法を見つけましたが、誰かが私にこの背後にあるルールを説明するグルーヴィーなドキュメントを教えてくれるかどうか興味があります。

具体的には....なぜ単純な割り当てになるのでしょうか

animal = 'bear' 

元の変数に影響しますか?ここでシャドウコピーが作成されていますか?

import org.junit.Test

/*
 * Author: cbedford
 * Date: 8/30/12
 * Time: 1:16 PM
 */

class GroovyTest {
    String animal = "cat"
    String name = "fred"

    @Test
    public void testDelegateWithModificationOfDelegateVariable() {
    String code = "{ ->   delegate.animal = 'bear';   return name + 'xx' ; }"
    def shell = new GroovyShell()
    def closure = shell.evaluate(code)

    closure.delegate = this
    def result = closure()

    println "result is $result"
    println "animal is $animal"

    assert animal == 'bear'
    assert result == 'fredxx'
    }


    // This test will FAIL.
    @Test
    public void testDelegateWithFailedModificationOfDelegateVariable() {
    String code = "{ ->   animal = 'bear';   return name + 'xx' ; }"
    def shell = new GroovyShell()
    def closure = shell.evaluate(code)

    closure.delegate = this
    def result = closure()

    println "result is $result"
    println "animal is $animal"

    assert animal == 'bear'
    assert result == 'fredxx'
    }
}
4

1 に答える 1

9

Groovy クロージャーには、クロージャー内のシンボルを解決するための5 つの戦略があります。

  • OWNER_FIRST: 所有者 (クロージャーが定義されている場所) が最初にチェックされ、次にデリゲートがチェックされます
  • OWNER_ONLY: 所有者がチェックされ、デリゲートは明示的に参照されている場合にのみチェックされます
  • DELEGATE_FIRST: デリゲートが最初にチェックされ、次に所有者がチェックされます
  • DELEGATE_ONLY: デリゲートが最初にチェックされ、所有者は明示的に参照されている場合にのみチェックされます
  • TO_SELF: デリゲートも所有者もチェックされていません

デフォルトは ですOWNER_FIRST。クロージャーは動的に定義されるため、所有者はそれ自体が特別なルールを持つ Script オブジェクトです。スクリプトanimal = 'bear'を記述すると、実際には呼び出された新しいバインディングが作成され、それanimalに割り当て'bear'られます。

次のように呼び出す前に、クロージャーの解決戦略を変更するだけで、デリゲートを明示的に参照せずにテストが機能するように修正できます。

closure.resolveStrategy = Closure.DELEGATE_FIRST

これにより、奇妙なスクリプト バインドが回避され、期待どおりにデリゲートが使用されます。

于 2012-08-30T20:51:50.243 に答える