This was just an experiment to see if I could replicate something like C++ function pointers in Java. Basically, the idea was to have an object which represented a method call, and when you call .invoke()
on that object, the method would run.
It feels a little sloppy to me, and I feel like there may be some tricks of Reflection and Generics that would make the code more elegant/usable which I haven't thought of or am not aware of. Any tips or tricks?
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.jpgohlke.util.logging.LogUtil;
public class MethodPointer<T, R> {
//Logger instance
private static final Logger LOGGER = Logger.getLogger(MethodPointer.class);
//Object fields
private final Method method;
private final Class<R> returnClass;
private ArrayList<Object> args = new ArrayList<Object>();
private T object = null;
//Constructors
public MethodPointer(Method method, Class<R> returnClass) {
this.method = method;
this.returnClass = returnClass;
}
public MethodPointer(Class<T> clazz, T object, String methodName, Class<R> returnClass, Class<?> ... paramClasses) {
Method theMethod = null;
try {
theMethod = clazz.getMethod(methodName, paramClasses);
}
catch(NoSuchMethodException nsme) {
LogUtil.log(LOGGER, Level.ERROR, "Unable to find method " + methodName + " in " + clazz.getSimpleName(), nsme);
throw new IllegalArgumentException("Method signature does not exist in " + clazz.getSimpleName());
}
method = theMethod;
this.object = object;
this.returnClass = returnClass;
}
public MethodPointer(Class<T> clazz, String methodName, Class<R> returnClass, Class<?> ... paramClasses) {
this(clazz, null, methodName, returnClass, paramClasses);
}
//Accessors and mutators
public Method getMethod() {
return method;
}
public Class<R> getReturnClass() {
return returnClass;
}
public Object[] getArguments() {
return args.toArray();
}
public void setArguments(Object ... args) {
this.args = new ArrayList<Object>(Arrays.asList(args));
}
public void setArguments(ArrayList<Object> args) {
this.args = args;
}
public T getObject() {
return object;
}
public void setObject(T object) {
this.object = object;
}
//Invoking the method
public R invoke() throws Exception {
if(Modifier.isStatic(method.getModifiers())) {
object = null;
}
else if(object == null) {
LOGGER.error("An object must be provided to invoke a non-static method upon.");
throw new IllegalArgumentException("An object must be provided to invoke a non-static method upon.");
}
try {
Object returnValue = method.invoke(object, args.toArray());
if(returnClass.isInstance(returnValue)) {
return returnClass.cast(returnValue);
}
else {
LOGGER.error("Unable to return value from method invocation; the object is not an instance of " + returnClass.getSimpleName());
return null;
}
}
catch(Exception e) {
LogUtil.log(LOGGER, Level.ERROR, "Unable to invoke method " + method.getName(), e);
throw e;
}
}
public R invoke(Object ... args) throws Exception {
this.args.addAll(Arrays.asList(args));
return invoke(returnClass);
}
}
args
should beList<Object>
rather thanArrayList<Object>
, if I'm adhering strictly to Java convention. – Jeff Gohlke Oct 21 '13 at 13:58