Alex Spurling
Alex Spurling

Reputation: 55934

Adding a property to a Groovy object from Java

I would like to be able to add a property to an instance of String object using Groovy's metaprogramming capabilities from Java.

To do this from Groovy is simple:

class GroovyClass {
    def getDynamicString() {
        def myString = "hello"
        myString.metaClass.dynamicProperty = "there"
        return myString
    }
}

Here is my Spock test case:

def "should add dynamic property"() {
    GroovyClass groovyClass = new GroovyClass()

    when:
    def theString = groovyClass.getDynamicString()
    then:
    theString == "hello"
    theString.dynamicProperty == "there"
}

However, I would like to do the same thing from Java and run it through the same test. As I understand, Groovy adds extra properties and methods (such as getMetaClass) to the JDK classes like String but I'm not sure exactly how or when it does this. I have found that the following solution works but it seems cumbersome to initialise a GroovyShell and to write the metaprogramming code as a string.

public String getDynamicString()
{
    String myString = "hello";
    GroovyShell shell = new GroovyShell();
    shell.setVariable("myString", myString);
    shell.setVariable("theDynamicPropertyValue", "there");
    shell.evaluate("myString.metaClass.dynamicProperty = theDynamicPropertyValue");
    return myString;
}

Is there a way to do the above without using shell.evaluate - i.e. by calling the Groovy library methods from Java directly?

Upvotes: 3

Views: 1417

Answers (1)

tim_yates
tim_yates

Reputation: 171184

So, given this groovy script:

def a = 'A String'

Decorator.decorate( a, 'foo', 'bar' )

assert a       == 'A String'
assert a.class == String
assert a.foo   == 'bar'

We can then write our java Decorator class like so:

import org.codehaus.groovy.runtime.HandleMetaClass ;
import org.codehaus.groovy.runtime.InvokerHelper ;

public class Decorator {
  public static void decorate( Object o, String name, Object value ) {
    HandleMetaClass hmc = new HandleMetaClass( InvokerHelper.getMetaClass( o ), o ) ;
    hmc.setProperty( name, value ) ;
  }
}

And then if we compile the java class, and run the groovy script, all the asserts should pass

Upvotes: 6

Related Questions