Metaprogramming is so Groovy
One of the hippest features of dynamic languages is the ability to modify fundamental aspects of the language at runtime — for example, Ruby’s open classes facilitate the addition of new methods at runtime– so for instance, you can easily add a blank? method to the String object. In Plain Jane Java you can’t actually add methods to Java’s String as it is a final class– what’s more, you can’t easily add methods to non-final classes at runtime either. With Groovy, however, you can.
Groovy has extensive metaprogramming facilities and one such addition is the ExpandoMetaClass, which enables you to do a host of copasetic things, including adding methods to normal Java objects (like String).
For example, imagine a domain object representing a runner in a race– a race can have 1 to many runners; consequently, a simple domain object for a runner could look like:
class Runner{
def firstName
def lastName
def age
def race
}
in which an instance of a Runner is connected to an instance of a Race (let’s keep this simple, man, so don’t worry about the fact that you think you can run in multiple races). Users go online and fill out a race application and invariably fail to capitalize the first character of proper names– hence, when race information is printed, some names appear inconsistent:
- emily Smith
- Gordon herch
- bart simpson
Race organizers don’t care that people enter in names inconsistently, but they do want the official race ballets to reflect consistent-ness, man.
Ideally, the Runner domain object should have a fullName method, which returns “Emily Smith” instead of “emily Smith”– you could put some logic into the fullName method itself to manipulate the firstName and lastName properties; however, what if you need that same sort of first character capitalization technique else where?
You could look to a 3rd party library– such as Apache commons-lang, which actually does contain a capitalize method. This would certainly solve your dilemma, but you do have another option: just add a capitalize method to the String class itself, man.
To do so, you need to reference the metaClass property that all classes in Groovy expose– once you have it, you can dynamically add methods to that class itself by defining a closure. For instance, adding capitalize method is as easy as writing
String.metaClass.capitalize = {
return delegate[0].toUpperCase() + delegate[1..<(delegate.length())]
}
As you can see, the delegate variable inside the closure is the instance of the outside object, which in my case is a String. Using some Groovy constructs, all I am doing is grabbing the first character of a String instance (via a positional construct) and calling toUpperCase to it, then I’m ensuring that the remaining characters (using an exclusive range) are returned– otherwise the capitalize method would return a single capitalized character.
I can verify things are copasetic via a handy assert:
assert "test".capitalize() == "Test"
I’m sure there are better, more efficient ways to code the capitalize method too (such as having the closure use commons-lang’s capitalize method!), suffice to say, man, this took all of 2 minutes.
Now, because it’s my bag, I can code my fullName method as follows:
def fullName(){
return firstName.capitalize() + " " + lastName.capitalize()
}
The beauty of Groovy, of course, is that the above code is one of a few ways that method could be written– the return statement isn’t required and I could have used GStrings to convey the intent of the method in a nicer manner (i.e. "${firstName.capitalize()} ${lastName.capitalize()}").
Metaprogramming in Groovy is, as you can see, baby, hip and indubitably easy– what’s more, this capability brings unbridled power to Java applications– take a look under the covers of Grails (and Rails, for that matter, to see Ruby’s copasetic flexibility in action) and you’ll see what I mean. Dig it?
Saturday 02 Feb 2008 | Andy | Groovy
Another way to “use” commons-lang’s capitalize is through a Category as demonstrated here (http://groovy.dzone.com/articles/metaprogramming-groovy-i)
Andy, you are always a step ahead
Ahh, good article on Categories, friend! Keep up the groovy work, Andres!
[…] The Disco Blog wrote an interesting post today on Metaprogramming is so GroovyHere’s a quick excerpt One of the hippest features of dynamic languages is the ability to modify fundamental aspects of the language at runtime — for example, Ruby’s open classes facilitate the addition of new methods at runtime– so for instance, you can easily add a blank? method to the String object. In Plain Jane Java you can’t actually add methods to Java’s String as it is a final class– what’s more, you can’t easily add methods to non-final classes at runtime either. With Groovy, however, you can. Groovy has e […]
[…] Original post by Andy […]
[…] Glover over at The Disco Blog wrote today about Metaprogramming with Groovy. The ability to add proprietary or domain-specific behavior to objects is a great feature of […]
Nice post! Only a little mistake here:
I think “… calling isUpperCase to it …” should read “… calling toUpperCase to it …”.
Florian- thanks for the hip correction! I took care of it.
Andy,
You’re Soooooo Java!
return firstName.capitalize() + ” ” + lastName.capitalize()
return “${firstName.capitalize()} ${lastName.capitalize()}”
Nice article:)
Oh you mentioned that!
Now I’m very embarrassed
You bet, John– as I was writing this posting I knew it was too Java for the Groovy elite so I made sure to throw that paragraph in there!! I agree, using hip GStrings reads more nicely! Thanks for reading, man, and keep up the copasetic work with Groovy, baby!
Brent Snook presented on this topic at the Melbourne Groovy User Group a couple of months back.
Check the slides… http://jamesladdcode.com/wp-content/uploads/2007/12/mopping-up-with-groovy-brent-snook1.zip
Contains some excellent examples of what can be done.