Groovy MetaClass: Overriding a Method Whilst Using the Old Implementation

| Comments

The need to add some functionality an existing method, but avoiding cutting and pasting the old implementation has come up a few times over the last week.

Sometimes, creating a subclass isn’t feasible, often because you don’t control all of the places where that class gets used.

Calling super() without the subclass

All you need to do is to get a reference to the old method. Getting methods from the metaClass is easy, if there’s only one method signature for that method, just use the “&” operator:

def oldToOctalString = Long.metaClass.&toOctalString

If there are multiple method signatures for a method, you’ll need to use the getMetaMethod method and pass in the signature of the method you want. This is probably the safest way to ensure that you’re getting the method that you think you’re getting.

println Long.metaClass.methods.findAll { == "compareTo" }.join("\n")
// prints:
// public int java.lang.Long.compareTo(java.lang.Long)
// public int java.lang.Long.compareTo(java.lang.Object)

def oldCompareTo = Long.metaClass.getMetaMethod("compareTo", [java.lang.Long] as Class[]) 

Now that you know how to get a reference to the method you want, you can redefine the method, while still letting the old method’s implementation do all the heavy lifting for you.

Here, we redefine the “plus” method on Integer to always work with absolute values, without actually getting our hands dirty in all that messy arithmetic:

def oldPlus = Integer.metaClass.getMetaMethod("plus", [Integer] as Class[]) = { Integer n ->
    return oldPlus.invoke( Math.abs(delegate), Math.abs(n) )

assert 4 == 2 + 2
assert 4 == -2 + -2 

If you don’t save the old method (or completely re-implement the logic for plus), you’ll end up in an infinite loop and throw a java.lang.StackOverflowError: = { Integer n ->
    return Math.abs(delegate) + Math.abs(n)

// DOESN'T WORK THROWS java.lang.StackOverflowError
assert 4 == -2 + -2 // FAILS!!!

This same kind of thing can be done with AOP, but that often feels more heavy than a little metaClass manipulation.