Wednesday, July 16, 2008

Groovy is ... well Groovy!

There are so many great things about Groovy from helper classes for Collections, XML/HTTP/SWING/etc builders, simple parsers, closures, a built in shell to run classes and groovy scripts, and many more. Since this is a blog and not a book, I wanted to highlight one interesting feature of Groovy that helped me understand the true concept of the "meta-language".

Groovy in a way generalizes many of the classes that most Java developers are accustomed to managing in day to day code. In Groovy we deal with POJOs, Plain Old Groovy Objects (POGOs), and interceptors. Groovy has provided a layer to capture each object type. In general this layer is called the Meta-Object Protocol (MOP).

Let's take each object type for a bit of a spin...

class TestMethods extends GroovyTestCase
void testPOJO()
def val = new String("test method call on POJO")
assertEquals "test method call on POJO" , val.toString()

void testInterceptedPOJO()
def val = new Integer (999)
Integer.metaClass.toString = {-> 'intercepted at testIntercepted test case' }
assertEquals "intercepted at testIntercepted test case" , val.toString()

void testInterceptable()
def obj = new InterceptableObject()
assertEquals 'intercepted existingMethod on object', obj.method1()
assertEquals 'intercepted existingMethod on object', obj.method2()
assertEquals 'intercepted existingMethod on object' , obj.existingMethod()
assertEquals 'intercepted existingMethod on object' , obj.nonExistingMethod()


// Interceptable Object
class InterceptableObject implements GroovyInterceptable
def invokeMethod(String name, args) {'intercepted existingMethod on object'}
def method1() {'method1 called'}
def method2() { 'method2 called' }

class GroovyObject
def method1() { 'method1' }
def method2() { 'method2' }
def closureProp = { 'closure called' }

Notice that there are several methods for intercepting objects based on the object type (POGO, POJOs, and interceptors) It is also possible to create closures at runtime which essentially injects a new method into an object.

The Interceptable class and testInterceptable method demonstrate a few ideas. The first is that invokeAll will overide all methods in an object. Both method1() and method2() will return "intercepted existingMethod on object" in these examples. Also, Groovy provides the existingMethod() and nonexistingMethod() which define closures for any existing or nonexisting method. This can be useful for catching generic cases that might not fall under a set of predefined methods.

There is a lot more to describe with MOP and dynamic Groovy, and more can be found here. Also, I hope to soon cover DSLs and Groovy which is itself a great topic.

No comments: