Groovy scope - how to access script variable in a

2019-01-17 19:46发布

问题:

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 MissingPropertyExceptions 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:

  1. Why can't I access a variable annotated with @Field inside method()?
  2. How can I refer to a script variable inside method()?

回答1:

When you have methods or statements outside of a class declaration in a groovy script, an implicit class is created. To answer your questions:

  1. 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.

  2. 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();


回答2:

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() {
  }
}


标签: groovy scope