Google
 

Tuesday, June 29, 2010

Java Callbacks using java.lang.reflect

Java does not have callbacks. Most commonly, I see people working around this by implementing the Observer Pattern, or simply having an interface for each method they need to use as a callback.

On a recent project, I felt the need for more direct callbacks in my Java code. Java's reflection package seemed to hold my answer. Given a Class, a method name, and (in the case of overloaded methods) a list of parameter types, you can retrieve an instance of the Method class. Then, given a list of arguments to pass to the method, it can be invoked via reflection.

Java reflection isn't particularly fast, so I recommend against this approach in applications where performance is important. But reflection works, so I present my Callback class here: package com.bs; import java.lang.reflect.*; /** * Implements callback functionality for Java. * Callbacks are implemented using reflection, so should * be avoided if possible. * * @author Brian Shields <http://clockworkgear.blogspot.com/> */ public class Callback { private Object parentObj; private Method method; private Class<?>[] parameters; public Callback(Class<?> clazz, String methodName, Object parentObj) { // Find a method with the matching name Method[] allMethods; try { allMethods = clazz.getMethods(); } catch(SecurityException se) { allMethods = new Method[0]; } int count = 0; Method single = null; for(Method m : allMethods) { if(m.getName().equals(methodName)) { single = m; count++; } // Can't have more than one instance if(count > 1) throw new IllegalArgumentException(clazz.getName() + " has more than one method named " + methodName); } if(count == 0) // No instances found throw new IllegalArgumentException(clazz.getName() + " has no method named " + methodName); this.parentObj = parentObj; this.method = single; this.parameters = single.getParameterTypes(); } public Callback( Class<?> clazz, String methodName, Object parentObj, Class<?>...parameters) { try { this.method = clazz.getMethod(methodName, parameters); } catch(NoSuchMethodException nsme) { nsme.printStackTrace(); } catch(SecurityException se) { se.printStackTrace(); } this.parentObj = parentObj; this.parameters = parameters; } public Object call(Object...vals) { if(parameters.length != vals.length) throw new IllegalArgumentException( "Wrong number of method parameters given. Found " + vals.length + ", expected " + parameters.length); Object ret = null; try { ret = method.invoke(parentObj, vals); } catch(IllegalAccessException iae) { iae.printStackTrace(); } catch(InvocationTargetException ite) { ite.printStackTrace(); } return ret; } }