Friday, April 11, 2008

Implementing your own little delegates for Java

Not having delegates in Java makes writing Eventhandlers etc. a pain from the syntax perspective. Nevertheless you can achieve something like a delegate with Java's reflection mechanisms.

Here is the problem I was working on:

I had a Manager class which managed a list of clients. The Manger class consisted mainly out of methods calling a method with the same name for each client and returning the first valid return value of the client.

class Manager {
List<Client> clients;

public String a(String param) {
for(Client client : clients) {
String retval = client.a(param)
if (retval != null) {
return retval
}
}
}
public String b(String param) {
for(Client client : clients) {
String retval = client.b(param)
if (retval != null) {
return retval
}
}
}
public String c(String param) {
for(Client client : clients) {
String retval = client.c(param)
if (retval != null) {
return retval
}
}
}

}



Well using some kind of Command pattern this could be solved a lot better, but the syntax would not be very readable.

I would be better to write something like:

class Manager {
List<Client> clients;

public String a(String param) {
return eachClient( a, param) );
}
public String b(String param) {
return eachClient( b, param) );
}
public String c(String param) {
return eachClient( c, param) );
}
}


The good news is in Java you can build this syntax (if you are willing to pay the performance price of reflection)

Here is how this works:

First create a eachClient method passing in the method name and the original parameters as parameters. Search for the method in the Client class. For each client execute the method.

Easy it sounds easy it is. Here is the eachClient method:

Object eachClient(String method, Object... params) {
for(Client client : clients) {
Method m = getMethod(client, method, params);
Object o = m.invoce(client, params);
if (o != null) {
return o
}
}
}


The getMethod method look like this:

Method getMethod(Object target, String methodName, Object... params)
throws SecurityException, NoSuchMethodException {
Class[] parameterTypes = new Class[params.length];

for (int i = 0; i < params.length; i++) {
parameterTypes[i] = params[i].getClass();
}
return target.getClass().getMethod(methodName, parameterTypes);
}
}


Note that the exception handling in eachClient is not mentioned here to make the examples more readable.

Note that getMethod will not find methods if the parameter objects class is an inherited class of the parameter class specified in the method signature.
Post a Comment