Search code examples
groovyfield

Using groovy.transform.Field to define another Field


I am writing a Groovy script, where I need to use some of my fields inside closures. Below is a very simplified example to demonstrate my problem.

#!/usr/bin/env groovy

import groovy.transform.Field

@Field def first = 'one'
assert first == 'one'

So far so good. In my real-world case, this will hold a String representing a server name. I then modify this "default" value from command-line options - using CliBuilder() - maybe something like:

first = 'two'
assert first == 'two'

This still works. I now need to create a new field, from the one above:

@Field def second = first
assert second == 'two'

In my real-world case, this will be an HTTPBuilder() that I will be making REST calls with.

This fails:

Assertion failed: 

assert second == 'two'
       |      |
       'one'  false

Is there a way to get this to work?

A failed attempt to somehow work around this. Using an intermediary, something like:

def intermediary = first
assert intermediary == 'two'
@Field def second = intermediary

Fails with:

Caught: java.lang.reflect.InvocationTargetException
java.lang.reflect.InvocationTargetException
    at something.main(something)
Caused by: groovy.lang.MissingPropertyException: No such property: intermediary for class: something
    at something.<init>(something)
    ... 1 more

Solution

  • the annotation @Field declares member of the script class and any other script commands will go to script.run() method

    for example the script

    import groovy.transform.Field
    
    @Field def first = 'one'
    first = 'two'
    @Field def second = first
    assert second == 'two' // <<<---- fails because second=='one'
    

    will be transformed approximately to this class during script compilation:

    class scriptXXXX{
        Object first
        Object second
        
        scriptXXXX(){ // constructor
            first = 'one'
            second = first
        }
    
        void run(){ // script body
            first = 'two'
            assert second == 'two' : null    
        }
    }
    

    You could see this in groovyconsole by pressing Ctrl+T

    enter image description here


    as a variant you could separate field declaration and initialization:

    @Field def first = 'one'
    @Field def second
    first = 'two'
    second = first
    assert second == 'two'