Groovy is very dynamic. We can add methods to classes at runtime that don't exist at compile time. We can add our own custom MetaClass
at startup time of our application if we follow the magic package naming convention. The naming convention is groovy.runtime.metaclass.[package].[class]MetaClass
. For example if we want to change the behavior of the java.lang.String
class, then we must write a custom MetaClass
with the package name groovy.runtime.metaclass.java.lang
and class name StringMetaClass
. We can do the same for classes we create ourselves. For example if we have a class myapp.RunApp
than the custom metaclass implementation RunAppMetaClass
would be in package groovy.runtime.metaclass.myapp
.
Our custom MetaClass
is extended from DelegatingMetaClass
and besides the name of the class and the package we can write our code the way we want.
We must first compile the custom MetaClass
and then we must put it in the classpath of the application code that is going to use the MetaClass
.
// File: StringMetaClass.groovy package groovy.runtime.metaclass.java.lang class StringMetaClass extends DelegatingMetaClass { StringMetaClass(MetaClass meta) { super(meta) } Object invokeMethod(Object object, String method, Object[] arguments) { if (method == 'hasGroovy') { object ==~ /.*[Gg]roovy.*/ } else { super.invokeMethod object, method, arguments } } }
The code that will use the delegating metaclass implementation:
// File: StringDelegateSample.groovy // Original methods are still invoked. assert 'mrhaki'.toUpperCase() == 'MRHAKI' // Invoke 'hasGroovy' method we added via the DelegatingMetaClass. assert !'Java'.hasGroovy() assert 'mrhaki loves Groovy'.hasGroovy() assert 'Groovy'.toLowerCase().hasGroovy()
First we compile our StringMetaClass
:$ groovyc StringMetaClass.groovy
.
Next we can run the StringDelegateSample.groovy
file if we put the generated class file in our classpath:$ groovy -cp . StringDelegateSample