groovy source file: Uh, hi compiler, I need to invoke a method on an object — the thing is, I’m not totally sure that the object has this method.
groovyc: Relax, man. No worries, I’ll just go ahead and generate some byte code for you.
groovy source file: Really? You’re not going to check to see if that method exists first?
groovyc: Static checking is for squares, man! We’ll just let the Runtime handle it.
groovy source file: Isn’t that dangerous?
groovyc: You’re starting to sound like my old man, Java, and you’re totally killing my buzz.
javac: I heard that! You kids with your dynamic method invocation! You’re going to get RuntimeExceptions all over yourselves!
Groovy supports Dynamic Method Invocation, that is, the ability to call a method dynamically at run time. So if we have an object like:
class SomeObject {
def someMethod() {
// do stuff
}
}
We can then call that method at run time dynamically like so:
String methodName = "someMethod"
new SomeObject()."$methodName"()
And that will all work out fine. Of course you can do the same thing in Java through reflection:
SomeObject obj = new SomeObject();
obj.getClass().getMethod(methodName).invoke(obj);
Provided you wrap that in a “try” block to catch the six or seven exceptions that could occur, you’re basically at the Groovy version.
It turns out however, that Groovy can also go the other way — that is, you can call a method on an object which that object does not even define. Apparently this feature is called “pretending”, and it’s kind of neat, so pull up a chair and read on.
I’m developing a Java application that will ultimately be maintained by the client’s internal IT staff. These guys are very strong database programmers and very knowledgeable of the details in their domain (the city’s budget). With six billion and change to be accounted for, there are organizations, bureaus, departments, and divisions flying all over the place with custom rules aplenty.
This situation seems to cry out for a DSL (Domain Specific Language) and that’s where Groovy comes in. These guys are already experts in the city’s complex organizational structure, it would be nice if that knowledge could translate to the code as seamlessly as possible.
So now that the stage is set, let’s look at a small example where Groovy Pretending can be useful:
In one section of this application, summaries need to be generated based on different parts of the budget. So budget line items are being sucked in with various categories and need to be totaled. No two summaries are the same, so some categories may have “contractual”, “land”, “other” (and my personal favorite “other 2″), while a different summary could have “land”, “equipment”, and “commodities.”
So one of the ways to handle this could be to have a “summary” object that keeps a running total of each category in a map. Something like this:
class Summary {
private Map<String, Long> map = new HashMap<String, Long>();
public void addTo(String category, long amount) {
if (!map.containsKey(category)) {
map.put(category, 0);
}
map.put(category, map.get(category) + amount);
}
}
So then to use this class:
summary.addTo("Contractual", contAmt);
summary.addTo("Equipment", equipAmt);
This is fine, it all works and doesn’t ask too much of someone not completely familiar with Java. However, in Groovy, will can turn it into this:
summary.addToContractual contAmt
summary.addToEquipment equipAmt
Now it looks slightly more like a DSL, but the kicker here is that the Summary class doesn’t have the methods “addtoContractual” or “addToEquipment”.
In Groovy, methods are dispatched via the object’s invokeMethod method. So if we override that method we get:
Object invokeMethod(String name, Object args) {
if (name.startsWith("addTo")) {
String methodName = name.substring("addTo".length())
if (!map.containsKey(methodName)) {
map.put(methodName, 0)
}
map[methodName] += args[0]
return
}
else if (name.startsWith("get")) {
def ret = map[name.substring("get".length())]
return ret != null ? ret : 0
}
return InvokerHelper.getMetaClass(this).invokeMethod(this, name, args)
}
In the above example, if the method to be invoked starts with “addTo”, it will hash the rest of the name (Contractual, or Equipment in this case), and add the argument to the mapped value. If the method name starts with “get” it will retrieve the associated value, again, using the remainder of the method name as the key. If neither case applies, the method will get invoked as normal.
So in a nutshell, we have this class where a client could call any sort of “addToX” and “getX” methods on this object, and the object will pretend that it actually has methods of that name.
Now, I’m not necessarily advocating this as good design practice. It’s no more type safe than the Java version (using Strings), and what’s potentially worse is that it gives the illusion of type safety since developers are used to compilers resolving method names.
I do, however, think it is an interesting feature of the language. Plus it paves the way for Groovy Builders, which I think are a good design practice. So stayed tuned for the Builder post…
Also, my apologies if after reading the title you were expecting a tutorial on building forts out of couch cushions while listening to Hendrix. That post will follow later in the week under the title “Dynamic Method Invocation with Dynamic Languages”.
Tags: Groovy
| M | T | W | T | F | S | S |
|---|---|---|---|---|---|---|
| « May | ||||||
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 | |||
Great post Sten. Please consider adding your feed to http://groovyblogs.org =)
Nice post actually. But you may want to somehow not use getX as it may conflict with other object getters. Or just call the usual get if your key is not in the map.
@Andres: Thanks for the kudos. I’ve been reading your blog for a while on jroller now. Thanks for stopping by.
@Ayman: That’s a great point about conflicting with the getters. I don’t necessarily recommend this example as good practice. I’m still not sure what I think of the whole “pretended” language feature.
Really a nice post.
But I have another question concerning this problem of calling unknown methods.
I have a String e.g.
String dothis = “hello(\”world\”)”;
Is it possible to call the method “hello” with the given parameter from the string without extracting the parameter and give it sepparated? I mean, am I forced to call the invokeMethod() with the parameters or is there a possibility to call the method as it is in the string?
Kind regards
Tobias
Hi Tobias,
If I think I understand your question, it might be answered in the following code:
class MyClass {
def hello(param) {
println “$param”
}
}
def obj = new MyClass()
String hello = “hello”
String helloWithArg = “hello(’world’)”
obj.hello(”world”) // this works
obj.”$hello”(”world”) // this works too
obj.”$helloWithArg” // this throws an exception (missing property)
So no, I don’t think you can pass in method parameters using String interpolation that way. You are correct in that you’d have to parse out the parameters.
Hi sanderson,
I thought I could add some further information here if anybody else has the same problem. I found out that one can execute any command out of a String via the Eval() method which is based on the GroovyShell!
Kind regards
Tobias