22150


Groovy を触り始めているものです。自分は、

  1. クロージャは `delegate` を設定することで、その `delegate` に対しても名前の検索を行うようになる
  2. クロージャは、その他に`owner` と `this` を持つ。`owner`は、そのクロージャが定義された「クロージャないしクラス」を指し示す
  3. クロージャは、`resolveStrategy` を `Clousure.DELEGATE_ONLY`に設定することで、`owner`への探索を抑制することができる

と理解しているのですが、次のコードを実行すると、自分の期待とは違う挙動をしています。

[code]

    class MyClass {
        def outerClosure = {
            def str = "outer"
            def nestedClosure = {
                println "delegate: ${delegate.class.name}"
                println "owner: ${owner.class.name}"
                println "this: ${this.class.name}"
                println "resolveStrategy: ${resolveStrategy}"
                println "Closure.DELEGATE_ONLY: ${Closure.DELEGATE_ONLY}"
                println(str)
            }
            nestedClosure
        }
    }
    
    class Hoge {
        def str = "hoge"
    }
    
    myObj = new MyClass()
    hoge = new Hoge()
    def nestedClosure = myObj.outerClosure()
    nestedClosure.delegate = hoge
    nestedClosure.resolveStrategy = Closure.DELEGATE_ONLY
    
    nestedClosure()
    
[/code]

出力:

[code]

    delegate: Hoge
    owner: MyClass$_closure1
    this: MyClass
    resolveStrategy: 3
    Closure.DELEGATE_ONLY: 3
    outer
    
[/code]

というのも、 `DELEGATE_ONLY`にもかかわらず、`owner`のスコープを探して
`outer`の`str`を見つけてきている様子なのですが、これは一体どうしてなのでしょうか。


CC BY-SA 3.0
2016-02-02T18:21:01.200
21642
2016-02-16T04:20:07.997
754
question
groovy
groovy のクロージャにおける、名前の解決がわからない(delegateを指定した場合の owner の挙動について)


`outerClosure()`を評価した時点で`outerClosure`のローカル変数の`str`が`nestedClosure`
にキャプチャされるので、`delegate`に設定した`Hoge`を探索する以前に`str`が`outerClosure`由来の変数として解決できている、ということじゃないでしょうか。

`DELEGATE_ONLY`の効果を確かめたい場合は次のようにすると良いと思います。

[code]

    class MyClass {
        // こっちに宣言しておくとDELEGATE_ONLYの効果がわかる
        def str = "myClassField"
    
        def outerClosure = {
            // ここに変数宣言してしまうとnestedClosureにキャプチャされてしまう。
            // delegateを探索する以前にこっちが見つかってしまう。
            //def str = "outer"
    
            def nestedClosure = {
                println "delegate: ${delegate.class.name}"
                println "owner: ${owner.class.name}"
                println "this: ${this.class.name}"
                println "resolveStrategy: ${resolveStrategy}"
                println "Closure.DELEGATE_ONLY: ${resolveStrategy == Closure.DELEGATE_ONLY}"
                return str
            }
            nestedClosure
        }
    }
    
    class Hoge {
        def str = "hoge"
    }
    
    def myObj = new MyClass()
    def nestedClosure = myObj.outerClosure()
    nestedClosure.delegate = new Hoge()
    
    assert nestedClosure() == "myClassField"
    
    nestedClosure.resolveStrategy = Closure.DELEGATE_ONLY
    
    assert nestedClosure() == "hoge"
    
[/code]


CC BY-SA 3.0
2016-02-16T04:20:07.997
22150
2016-02-16T04:20:07.997
2356
21642
answer