One of Groovy's strengths is its ability to decrease clue code and API flaws by providing additional API methods for JDK classes.
For example, instead of creating a reader and reading a file of certain encoding, Groovy enriches java.io.File with a #getText(String charset) method.
In fact, the following Groovy code to read a text file (yeah, we all have seen this code in Java more than a couple of times) is:
deff=newFile(...)deftext=f.text('utf-8')
Before 2.0, these methods have all internally been centralised in a class called DefaultGroovyMethods [0], thus the methods defined within this class have been called DGM methods.
Groovy 2.0 not only split the DGM class into multiple classes: ResourceGroovyMethods, IOGroovyMethods, StringGroovyMethods etc. but provides a way to add custom methods to JDK classes through so-called extension modules.
Defining Extension Modules
Extension modules themselves define static or instance methods. We start with instance methods. Let us assume we wanted to enrich the java.util.GregorianCalendar which implements java.util.Calendar with custom methods, e.g. a unique month code that is computed by YEAR * 100 + MONTH.
First of all, we need to create a Groovy or Java class. The class name does not need to comply to a naming convention, so we'll name the class MyCalendarExtensions.
The first parameter of an extension instance method always has to be the self parameter. It refers to the object this extension method is called on. Although every single method in this class is defined as a static method, the Groovy compiler will create instance methods for every single method and attach it to the type of the self parameter. It would be possible to bundle extension methods for different target types in a single extension module, although this is not considered clean style.
In order to detect the additional methods during compilation, an additional file is needed in META-INF/services: org.codehaus.groovy.runtime.ExtensionModule
[sourcecode]
moduleName=calendar-module
moduleVersion=1.0-ast
extensionClasses=org.ast.MyCalendarExtensions
[/sourcecode]
The module name and module version information is needed to detect conflicting module versions during the registration of all extension modules in the class path.
After having added the meta-information, we can write a test-case for the newly added getMonthCode() Calendar method:
As can be seen in the code sample above, the meta methods lead to much cleaner code and add the month code computation to every Calendar instance that is around in our project.
If we wanted to implement this trivial example in Java, we would have ended up with a separate *Util class and the usual Calendar.get(...) method calls.
If we wanted to add static methods, we would have to define a separate Groovy or Java class and add the appropriate meta-information to the META-INF file:
[sourcecode]
staticExtensionClasses=org.ast.MyStaticCalendarExtensions
[/sourcecode]
Note that even we added a static method, the self parameter is still needed to find out about the class the static method has to be added to.
As the majority in "extensionClasses" implies, extensionClasses and staticExtensionClasses might hold a comma-separated list of multiple extension definition classes.
Conclusion
Extension modules are an easy way to avoid a handful utility classes within a project. By defining Groovy or Java classes and providing some meta-data, JDK classes can be enriched by custom instance and static methods.