Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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);
    }

}
share|improve this question
    
I just realized that the type of args should be List<Object> rather than ArrayList<Object>, if I'm adhering strictly to Java convention. –  Jeff Gohlke Oct 21 '13 at 13:58
    
This could be called a thunk. However, be aware that the term is overloaded. –  200_success Oct 21 '13 at 16:27
    
@200_success Thanks! Learn something new every day. –  Jeff Gohlke Oct 21 '13 at 17:31
add comment

1 Answer

up vote 4 down vote accepted

It seams like there is a lot of work going on there for just a simple concept. I suspect you don't need all the flexibility and structure that this implementation gives you. Instead, I suggest you use a simple interfaces. Here is the Function interfaces that Guava uses:

public interface Function<F,T> {
    T apply(F input);
    boolean equals(Object object);
}

All you have to do is implement apply() and you are done. If it is a one off, then you can just create an anonymous class. If you are calling the same type of function repeatedly in a number of places or you need something slightly different, you can make a concrete class for those cases.

The good news is that Java 8 will make this much easier. The introduction of lambdas and method references makes it much easier to pass code to be called into a function. The Method Reference page has examples of lambdas and method references.

share|improve this answer
    
Interesting. Thanks. I'll take a look at Guava. I know I didn't need all the infrastructure, but I like to make my code as flexible as possible for the client to have easier API usage (like the multiple constructors, etc.) –  Jeff Gohlke Oct 21 '13 at 15:13
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.