Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy clash between automatic property getter, local variables, and closures?

Groovy seems to have some very unpleasant behavior associated with "Groovy Beans" and closures that can cause it to mask local variables in certain circumstances.

Is this known behavior and is there some documentation somewhere that details this stuff? I wasted a lot of time trying to figure out what wasn't working...

Consider the following code:

class TestClass {

    def doIt(Closure closure) {
        closure.setDelegate(this)
        closure.call()
    }

    def getRevision() {
        return "testclass revision"
    }
}

def testIt() {
    def revision = "testit revision"

    println "1: " + revision + " (expect: testit)"

    TestClass tester = new TestClass()
    tester.doIt {
        println "2: " + getRevision()  + " (expect: testclass)"
    }

    println "3: " + revision + " (expect: testit)"

    tester.doIt {
        println "4: " + revision + " (expect: testit)"
        revision = getRevision()
        println "5: " + revision + " (expect: testclass)"
        println "6: ${getRevision()} (expect: testclass)"
        println "7: " + getRevision() + " (expect: testclass)"
    }

    // expect to have been set to testclass value in previous closure
    println "8: " + revision + " (expect: testclass)"

    tester.doIt {
        println "9: ${getRevision()} (expect: testclass)"
        println "10: " + getRevision() + " (expect: testclass)"
    }

    println "11: " + revision + " (expect: testclass)"
}

testIt()

Running this code produces the following output:

1: testit revision (expect: testit)
2: testclass revision (expect: testclass)
3: testit revision (expect: testit)
4: testit revision (expect: testit)
5: testit revision (expect: testclass)
6: testit revision (expect: testclass)
7: testit revision (expect: testclass)
8: testit revision (expect: testclass)
9: testclass revision (expect: testclass)
10: testclass revision (expect: testclass)
11: testit revision (expect: testclass)

My main problem is with 5/6/7. It seems that simply using the local revision variable within the closure "hides" the getRevision() method in the delegate and replaces it with a bean-style auto-generated getRevision() to match the revision "property". If I don't use the revision variable, calls to getRevision() do not invoke this bean behavior.

Any insight or links to documentation would be appreciated!

like image 636
Will Harris Avatar asked Apr 26 '12 17:04

Will Harris


1 Answers

Groovy closures have a resolveStrategy property that can be used to control the resolution order. The default strategy is to look-up the property from the "owning" object and only if it cannot be found there to use the delegate. I believe that setting this to DELEGATE_FIRST will get the behavior you are expecting.

like image 108
ig0774 Avatar answered Oct 31 '22 04:10

ig0774