Reputation: 55934
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
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