Goodies in Groovy from DefaultGroovyMethods

Posted by & filed under , .

groovy-langIf you ever programmed in Groovy language, you probably “enjoyed” (maybe without realising) the joys of DefaultGroovyMethods. What you probably don’t realise is that you can override these methods to customize your classes — and occasionally generate some code that’s not that easy to read. (Do you remember the old C++ way of overriding operators and generate code that no one else could read apart from you? :D)

For instance, look at the following code:

def arr = [1, 2, 3]
def arr2 = arr * 2

Those of you who have been using Groovy for a while would know this is not the same as

def arr2 = [2, 4, 6]

but in fact it’s the same as

def arr2 = [1, 2, 3, 1, 2, 3]

Surprised? 🙂

You shouldn’t be! The GroovyDoc for this method explains it all:

Create a Collection composed of the elements of this Iterable, repeated a certain number of times. Note that for non-primitive elements, multiple references to the same instance will be added.

(The * operator “maps” to multiply method btw.)

OK, so then what do you to change that behaviour for your collection classes? Well, quite simply, you have to override the multiply method — for instance:

class MyCollection extends ArrayList {
    Collection multiply(Number factor) {
        this.collect { it -> it * factor }
    }
}

Now let’s use this:

def arr = [1, 2, 3] as MyCollection
def arr2 = arr * 2

arr2 now evaluates to … (drum roll 🙂 ) [2, 4, 6] !

We can take this even further — consider for instance the asBoolean method: this gets invoked when your class gets cast to a boolean; and example of this situation is when you use an instance of your class in an if statement:

def arr2 = [1, 2, 3] as MyCollection
if( arr2 ) {
    // ... do stuff
}

This will always evaluate to truthy “true” — as in this case the collection is not null and not empty. But what if you want your check to return true only if each and every one of the elements is > 0 (which evaluates to truthy “false” btw)? Simply: you just override asBoolean like this:

class MyCollection extends ArrayList {
    boolean asBoolean() {
        this.every { it > 0 }
    }
}

Now this

def arr2 = [1, 2, 3] as MyCollection
if( arr2 ) {
    // ... do stuff
}

evaluates to true, whereas

def arr2 = [1, 0, 3] as MyCollection
if( arr2 ) {
    // ... do stuff
}

evaluates to false.

Now as I said, you can do some mean things with the DefaultGroovyMethods — please don’t go nuts about operator overriding! 🙂 — but things like above come in handy at times.