3
votes

Question

Why does methodA() print set within run instead of set within closure?

Code example

class GroovyClosureVariableScopingTest extends Script {

    String s

    Object run() {
        s = "set within run"
        println "value of s in run(): $s"

        [1].each {
            s = "set within closure"
            println "value of s in each()-closure: $s"
            methodA()
        }
    }

    void methodA() {
        println "value of s in methodA(): $s"
    }
}

Actual output

value of s in run(): set within run
value of s in each()-closure: set within closure
value of s in methodA(): set within run            // <- Surprised to see the original value

Expected output

value of s in run(): set in run
value of s in each()-closure: set within closure
value of s in methodA(): set within closure        // <- Whould have expected this value

Elaboration

I haven't quite understood how variable scoping works in the above example. I would have expected s to be a property (a public instance variable) of the class and I thought that I was assigning it a value from within the each-closure which would be retained. But that does not seem to be the case.

Why does s not keep the value which it was assigned from within the closure?

Solution / workaround

Passing s as argument to methodA() works. See lines commented with // Change.

class GroovyClosureVariableScopingTest extends Script {

    String s

    Object run() {
        s = "set within run"
        println "value of s in run(): $s"

        [1].each {
            s = "set within closure"
            println "value of s in each()-closure: $s"
            methodA(s)              // Change
        }
    }

    void methodA(s) {               // Change
        println "value of s in methodA(): $s"
    }
}

Reference

Closures - Formal Definition

At the same time, the variables are still available normally to the enclosing scope, so the closure may read/change any such values, and code from the outer scope may read/change the same variables.

Versions

  • Groovy 1.8.6
  • Groovy-Eclipse plugin 2.8.0
  • Eclipse Platform 3.8.1
1
what groovy version is this and how do you run this class's code? with 2.3.6 and a slightly simpler version I get the expected result. With your code (without extending Script) it also works as expected)cfrick
@cfrick I've added the information to the question above.Lernkurve
might as well be the older verison or a bug there. have you tried with setS(...)?cfrick
Can you try Groovy 1.8.9?Aaron Digulla
@cfrick It indeed works with setS("set within closure"). Strange. :-/ Thank you!Lernkurve

1 Answers

0
votes

After user cfrick confirmed that it works on his machine, I ran it from the console and it indeed works.

From console

C:\temp>java -jar C:\...\lib\groovy-all-1.8.6.jar C:\temp\GroovyClosureVariableScopingTest.groovy
value of s in run(): set within run
value of s in each()-closure: set within closure
value of s in methodA(): set within closure // <- Here

With clean Eclipse installation

But I still don't get the original script to run with a clean installation of

Summary

I still don't know why it doesn't work -- even with a clean installation of Eclipse. However, it works from the console, it works on cfrick's machine and there is a workaround.