2012-08-30 12 views
8

Groovyのクロージャーとデリゲートを使って何かに遭遇しましたが、私はその言語の公式な部分か、おそらくバグだとは確信していません。groovyクロージャがデリゲートのスコープで定義された変数を変更するには、delegate.theVariableNameを明示的に指定する必要がありますか?

私は、外部ソースからの文字列として読み込んだクロージャーを定義しています。 クロージャーを定義するクラス内の変数の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' 
    } 
} 

答えて

7

グルービークロージャは、クロージャ内部シンボルを解決するためfive strategiesを有する:

  • OWNER_FIRST:所有者がチェックされている:(クロージャが定義されている)の所有者は、その後、デリゲート
  • OWNER_ONLY、最初にチェックされ委任先は明示的に参照されている場合のみチェックされます
  • DELEGATE_FIRST:委任先が最初にチェックされ、所有者が
  • DELEGATE_ONLY:デリゲートが最初にチェックされているが、明示的に
  • TO_SELF参照した場合、所有者のみがチェックされます。どちらのデリゲートも所有者が

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

あなたはでそれを呼び出す前に、単に閉鎖に解決戦略を変更することで、明示的にデリゲートを参照せずに動作するようにテストを修正することができます:

closure.resolveStrategy = Closure.DELEGATE_FIRST 

このスクリプトは、結合奇数を避け、期待通りにデリゲートを使用します。 。

+0

これは理にかなっています。速すぎる; ^)。本当にありがとう。 –

関連する問題