I have a question about scoping rules in Groovy. In the following snippet, I have three variables, a
has local scope, b
has script scope, and c
should get script scope as well using the @Field
annotation.
#!/usr/bin/groovy
import groovy.transform.Field;
//println org.codehaus.groovy.runtime.InvokerHelper.getVersion()
def a = 42;
b = "Tea"
@Field def c = "Cheese"
void func()
{
// println a // MissingPropertyException
println b // prints "Tea"
println c // prints "Cheese" with groovy 2.1.2, MissingPropertyException with groovy 1.8.6
}
class Main
{
def method()
{
// println a // MissingPropertyException
// println b // MissingPropertyException
// println c // MissingPropertyException with both 1.8.6. and 2.1.2
}
}
func();
new Main().method();
I get MissingPropertyException
s on the lines indicated with comments. The exceptions on a
are expected, as that variable has local scope. But I would expect b
to be accessible inside method()
- it isn't.
@Field
doesn't do anything in groovy 1.8.6, although after upgrading it works, so I guess that is an old bug. Nevertheless, c
is inaccessible inside method()
with either version.
So my questions are:
- Why can't I access a variable annotated with
@Field
inside
method()
?
- How can I refer to a script variable inside
method()
?
When you have methods or statements outside of a class
declaration in a groovy script, an implicit class is created. To answer your questions:
In your example, func()
can access the field c
because they are both members of the implicit class. The Main
class is not, so it can't.
You need to pass a reference to the script variable to method()
. One way is to pass the implicitly defined binding
object, through which you can access all the script scope variables.
Example:
#!/usr/bin/groovy
import groovy.transform.Field;
//println org.codehaus.groovy.runtime.InvokerHelper.getVersion()
def a = 42;
b = "Tea"
@Field def c = "Cheese"
void func()
{
// println a // MissingPropertyException
println b // prints "Tea"
println c // prints "Cheese" with groovy 2.1.2, MissingPropertyException with groovy 1.8.6
}
class Main
{
def scriptObject
def binding
def method()
{
// println a // MissingPropertyException
println binding.b
println scriptObject.c
}
}
func();
new Main(scriptObject: this, binding: binding).method();
This script and Main
are generated as two separate classes inside the same file.
As Main
is not an internal class of the Script class, it cannot see the java.lang.Object c
field inside the script class.
You would either have to explicitly wrap this script in a class with a static main( args )
method (and an internal Main
class) or you would need to pass an instance of the script class to the method like: Main.method( this )
This is the sort of thing that the above script generates:
class Script032034034 {
Object c
Script032034034() {
c = 'Cheese'
}
Object run() {
Object a = 42
b = 'Tea'
func()
new Main().method()
}
void func() {
println b
println c
}
}
class Main {
Object method() {
}
}