Overriding invokeMethod, getProperty and setProperty

ExpandoMetaClass - Overriding invokeMethod, getProperty and setProperty

It is also possible to override the methods invokeMethod, getProperty and setPropety on ExpandoMetaClass thus allowing even more dynamic behaviour.

Overriding invokeMethod

As an example of overring invokeMethod, take this simple example:

class Stuff {
   def invokeMe() { "foo" }
}

Stuff.metaClass.invokeMethod = { String name, args ->
   def metaMethod = Stuff.metaClass.getMetaMethod(name, args)
   def result
   if(metaMethod) result = metaMethod.invoke(delegate,args)
   else {
      result = "bar"
   }
   result
}

def stf = new Stuff()

assert "foo" == stf.invokeMe()
assert "bar" == stf.doStuff()

So what is happening here? Well firstly we've overriden invokeMethod by assigning it an appropriate closure, but in addition we first look-up a MetaMethod with the line:

def metaMethod = delegate.class.metaClass.getMetaMethod(name)

A MetaMethod in Groovy is a method that is known to exist on the MetaClass whether added at runtime or whatever, thus we check if there is an existing MetaMethod and if there isn't we simply return "bar", hence the behaviour of the assert statements is correct.

Overriding getProperty and setProperty

Again overriding getProperty and setProperty is similar to the above:

class Person {
   String name = "Fred"
}

Person.metaClass.getProperty = { String name ->
   def metaProperty = Person.metaClass.getMetaProperty(name)
   def result
   if(metaProperty) result = metaProperty.getProperty(delegate)
   else {
      result = "Flintstone"
   }
   result
}

def p = new Person()

assert "Fred" == p.name
assert "Flintstone" == p.other

The important thing to note here is that instead of a MetaMethod we look-up a MetaProperty instance if that exists we call the getProperty method of the MetaProperty passing the delegate (ie the instance of the class).

The only different with setProperty is you need the value in the method signature and to call setProperty on the MetaProperty:

Person.metaClass.setProperty = { String name, value ->
   ...
   if(metaProperty) metaProperty.setProperty(delegate, value)
   ...

}

Useful References