Hi,

I've been caught out by this in the past as well. It's actually quite
simple...

A Groovy Script like:

   String test

   void func(){
        println(test)
   }

   test = 'hello'
   func()

Gets compiled to a class as:

   class MyScript extends Script {
        def run() {
            String test

            test = 'hello'
            func()
        }

        void func() {
            println(test)
        }
   }

All "lose code" gets collected into the run() method, and functions
defined in your script are copied into the class as methods. As you can
see, the declaration "String test" ends up as part of the run() method,
and therefore the "test" variable is scoped locally to that run()
method. It's not a class member. Your func() method is not able to see it.

You can work around that by annotating the "String test" declaration
with the @Field annotation:

   @Field String test

   void func() {
        println(test)
   }

   test = 'hello'
   func()

This will cause the "String test" declaration to be included in the
Script class as a class member:

   class MyScript extends Script {
        String test

        def run() {
            test = 'hello'
            func()
        }

        void func(){
            println(test)
        }
   }

This is described in
https://docs.groovy-lang.org/latest/html/documentation/core-metaprogramming.html#xform-Field.
It is also very lightly mentioned in
https://groovy-lang.org/structure.html#_variables, but that page
probably doesn't make it clear enough.

Maarten

On 14/10/2020 15:45, Anton Shepelev wrote:
Hello, all

I tried to define a global variable in a Groovy script.
Section 1.1. of the "Semantics" documents says that one way
to define a variable is via its type, so I defined one in
the global scope of my script:

    String test

    void func()
    {
       println(test)
    }

    test = 'hello'
    func()

but it failed with the error: "No such property: test for
class: Script1". That didn't explain to me what the problem
was, but I learned that the script was looking for a
property named `test' in the global autogenerated Script
class. OK, thought I, I will give it what it asks, and
cosulted section 1.6.2. (Properties) of the "Object
orientation" document, which told me that the definition of
a property is identical to the definition of a variable I
used above, i.e. that

    String test

defienes a string property `test' or a string variable
`test', depending on context. I was now stuck and resorted
to an internet search, which brought up the following page

    Groovy Variable Scope:
    https://www.baeldung.com/groovy/variable-scope

where I read:

    Scopes in Groovy follow, above all, the rule that all
    variables are created public by default. This means that,
    unless specified, we'll be able to access any variable we
    created from any other scope in the code.

Why, then, is `test' inaccessible from the function in my
script?  And below:

    The easiest way to create a global variable in a Groovy
    script is to assign it anywhere in the script without any
    special keywords. We don't even need to define the type:

       x = 200

After I removed the "String test" line from my script it
started to work, but why?  I returned to the "Semantics"
document and read section 1.2. (Variable assignment) to see
whether the assignment operator may have the side effect of
defining a missing variable, but there is no indication that
it has.

Can you please explain to me, preferable with the
correspoding references to official documentation, why my
scirpt does not work whereas the modified one does?

Reply via email to