views:

301

answers:

4

Given:

class A
{
    public void m(List l) { ... }
}

Let's say I want to invoke method m with reflection, passing an ArrayList as the parameter to m:

List myList = new ArrayList();
A a = new A();
Method method = A.class.getMethod("m", new Class[] { myList.getClass() });
method.invoke(a, Object[] { myList });

The getMethod on line 3 will throw NoSuchMethodException because the runtime type of myList is ArrayList, not List.

Is there a good generic way around this that doesn't require knowledge of class A's parameter types?

+1  A: 

Instead of myList.getClass(), why not just pass in List.class? That is what your method is expecting.

Matt Ball
I don't actually have knowledge of exactly what method is being called. There isn't just `m`, there are many methods with different parameter types.
Jonathon
You need to know a-priori what you are going to call. Reflection is not like magic. You need to what method name and method signature you want to access via reflection. Sometimes you also need to know what version of a method you need to call (.ie. you need to access the version of a method 'm' defined by one of your super classes.
luis.espinal
+4  A: 

If you know the type is List, then use List.class as argument.

If you don't know the type in advance, imagine you have:

public void m(List l) {
 // all lists
}

public void m(ArrayList l) {
  // only array lists
}

Which method should the reflection invoke, if there is any automatic way?

If you want, you can use Class.getInterfaces() or Class.getSuperclass() but this is case-specific.

What you can do here is:

public void invoke(Object targetObject, Object[] parameters,
        String methodName) {
    for (Method method : targetObject.getClass().getMethods()) {
        if (!method.getName().equals(methodName)) {
            continue;
        }
        Class<?>[] parameterTypes = method.getParameterTypes();
        boolean matches = true;
        for (int i = 0; i < parameterTypes.length; i++) {
            if (!parameterTypes[i].isAssignableFrom(parameters[i]
                    .getClass())) {
                matches = false;
                break;
            }
        }
        if (matches) {
            // obtain a Class[] based on the passed arguments as Object[]
            method.invoke(targetObject, parametersClasses);
        }
    }
}
Bozho
My thoughts exactly.
Matt Ball
I don't know the type in advance. I would expect `m2` in your example if my l.getClass() returns ArrayList as the runtime type, but maybe I'm expecting too much?
Jonathon
exactly. But you want to execute the first method depending on whether the second exists or not. You can't do that.
Bozho
So the only way to actually get `m1` would be try various permutations of the superclasses and interfaces for the various parameters which isn't a very good idea.
Jonathon
@Jonathon check my update
Bozho
Exactly, you don't want to do all those permutations. In some very specific cases - .ie. you are building a framework or interpreter, or need to interface with (possibly badly written) p3rd party libraries, you need to do that. However, the resulting code is very brittle. Reflection is just another way to access artifacts defined by a class. In this case, the access is dynamic rather than statically compiled. But the requirements to know what you need to call remain the same.
luis.espinal
I'll also add that if you truly do not know the actual type, and the requirements need you to operate without knowing that type of information, I'd say this: proceed with caution. In general, I would take such a situation like a *code (or requirement) smell*, something that needs revisiting.
luis.espinal
@Bozho I think that's exactly what I need to do -- I have the name, so I won't have to check every method in the class, either. Thanks!
Jonathon
@luis.espinal your points are noted, thanks!
Jonathon
+1 for the awesome reflection example, and pointing out the proper way to determine the correct method to call.
aperkins
+1  A: 

I'm guessing you want getDeclaredMethods(). Here is an example. You can dig through the list of methods and pick the one you want by name. Whether or not this is robust or a good idea is another question.

Sean Owen
Thanks, I did know about this but I didn't want to resort to it. I might have to, though.
Jonathon
A: 

See java.beans.Expression and java.beans.Statement.

EJP