solstice333
solstice333

Reputation: 3659

Groovy method cannot access variable in enclosing scope

I know that this is what closures are for, but shouldn't the below work as well?:

def f = 'foo'
def foo() {
   println(f)
}
foo()

It results in:

Caught: groovy.lang.MissingPropertyException: No such property: f for class: bar
groovy.lang.MissingPropertyException: No such property: f for class: bar
   at bar.foo(bar.groovy:4)
   at bar.run(bar.groovy:7)

Upvotes: 8

Views: 4398

Answers (1)

Matias Bjarland
Matias Bjarland

Reputation: 4482

In a groovy script (as opposed to class), your code is essentially equivalent to:

class ScriptName {
  def main(args) {
    new ScriptName().run(args)
  }

  def run(args) {
    def f = 'foo'
    foo()
  }

  def foo() {
    println(f)
  }
}

the 'implicit' enclosing class created by groovy for groovy scripts is always present but not visible in your code. The above makes it obvious why the foo method does not see the f variable.

You have a couple of options

option 1 - binding

See the groovy docs on script bindings for details.

// put the variable in the script binding
f = 'foo'

(note that we do this without the def keyword)

this is shorthand for:

binding.setVariable('f', 'foo')

where the script binding is visible everywhere for groovy scripts and this makes the variable essentially 'global'.

option 2 - @Field annotation

See groovy docs on the Field annotation for details.

import groovy.transform.Field
...    
// use the groovy.transform.Field annotation 
@Field f = 'foo' 

the Field annotation is specifically there to give groovy scripts the ability to add fields to the 'implicit' enclosing class. The generated class would then look something like the following:

class ScriptName {
  def f = 'foo'

  def main(args) {
    new ScriptName().run(args)
  }

  def run(args) {
    foo()
  }

  def foo() {
    println(f)
  }
}

which also essentially makes the variable accessible 'globally' in the script.

Upvotes: 21

Related Questions