September 29, 2009

Groovy Goodness: Use Categories to Add Functionality to Classes

Categories allow us to add extra functionality to classes, even those classes we didn't develop ourselves. A Category class contain static methods. The first argument of the method determines the type the method can be applied to.

We can also use the @Category transformation annotation to make a class into a Category. The class doesn't have to define static methods anymore, but we can use instance methods. The parameter of the @Category annotation defines for which class the Category is.

The Category can be applied to a specific code block with the use keyword. Only within the code block the rules of the Category are applied.

class Speak {
    static String shout(String text) {  // Method argument is String, so we can add shout() to String object.
        text.toUpperCase() + '!'
    static String whisper(String text, boolean veryQuiet = false) {
        "${veryQuiet ? 'sssssssh' : 'sssh'}.. $text"
    static String army(String text) {
        "$text. Sir, yes sir!"

use (Speak) {
    assert 'PAY ATTENTION!' == "Pay attention".shout()
    assert 'sssh.. Be vewy, vewy, quiet.' == "Be vewy, vewy, quiet.".whisper()
    assert 'sssssssh.. Be vewy, vewy, quiet.' == "Be vewy, vewy, quiet.".whisper(true)
    assert 'Groovy rocks. Sir, yes sir!' == "Groovy rocks".army()

// Or we can use the @Category annotation.
class StreetTalk {
    String hiphop() {
        "Yo, yo, here we go. ${this}"

use(StreetTalk) {
    assert 'Yo, yo, here we go. Groovy is fun!' == 'Groovy is fun!'.hiphop()

// We can use static methods from other Java classes.
use (org.apache.commons.codec.digest.DigestUtils) {
    assert '900150983cd24fb0d6963f7d28e17f72' == 'abc'.md5Hex()