Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy method cannot access variable in enclosing scope

Tags:

groovy

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)
like image 659
solstice333 Avatar asked Feb 08 '17 15:02

solstice333


1 Answers

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'

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.

like image 116
Matias Bjarland Avatar answered Sep 22 '22 00:09

Matias Bjarland