Thursday, March 4, 2010

Groovy Goodness: Invoke Methods Dynamically

In Groovy we can invoke a method just like in Java: we simply type the name of the method in our code. But what if we want to invoke a method name, but we don't know the name yet when we write the code, but we do know it at runtime? We can use the Reflection API for example to invoke the method, but in Groovy it can be shorter. In Groovy we can use a variable value for method invocation with a GString. The GString is evaluated at runtime and the result must be the method name we want to invoke.

class Simple {
    def hello(value) {
        "Hello $value, how are you?"
    def goodbye() {
        "Have a nice trip."

def s = new Simple()
def methods = ['hello', 'goodbye']

assert 'Hello mrhaki, how are you?' == s."${methods[0]}"('mrhaki')
assert 'Have a nice trip.' == s."${methods[1]}"()


Anonymous said...

Good to see you are posting again - I've missed your daily tidbits of Groovy!

mrhaki said...

Hi, thanks. I will be posting more regularly from now on. Probably each workday.

jquery said...

It was interesting to read this article and I hope to read a new article about this subject in your site in the near time.

Ravi Shekhar said...

Can we instantiate an object of a class if we have the class's name in String?

Hubert Klein Ikkink said...

@Ravi Shekhar: You can via a little detour. You must first convert the String value to a Class using as Class. Then you can invoke newInstance() method to invoke the constructor. See the following example:
def i = 'java.lang.Integer' as Class
assert i.newInstance(42) == 42I

Cohen said...

I was having trouble with this code in Groovy 2.3.5.

The problems were with the assert statements, use of expressions inside Gstrings, and dynamic invocation syntax.
Although the code was passing the asserts, it was not in the way intended.

The asserts were evaluating the string literals as booleans, which evaluated to true as they were not empty strings. Therefore, the second argument of the assert was never evaluated as the second argument is an error message displayed when the assert fails. We need to use comparison (==) instead of a comma.

The expressions were also not evaluating as expected. Instead of getting a string representation of the ith element of the array, it was getting the entire array's string representation with "[i]" concatenated. This can be solved by using curly braces to specify what is to be evaluated.

Finally, the invocation of methods without arguments still requires (empty) parentheses.

In summary, the final two lines should be:
assert 'Hello mrhaki, how are you?' == s."${methods[0]}"('mrhaki')
assert 'Have a nice trip.' == s."${methods[1]}"()

Hubert Klein Ikkink said...

@Cohen. Thank you for spotting the errors in the sample code. Kind of embarrassing, because the post is already old ;-) I've changed the sample code so it works as I intended.

Post a Comment