17

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?

4 Answers 4

21

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);
        }
    }
}
Sign up to request clarification or add additional context in comments.

7 Comments

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?
exactly. But you want to execute the first method depending on whether the second exists or not. You can't do that.
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.
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.
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.
|
2

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

Comments

1

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

11 Comments

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.
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: I agree with your point saying reflection isn't going to help you choose a method, but I disagree that you need to know a-priori which exact method you are going to call. In an interpreter / dynamic language context, all you have is a method name, arguments, and if you're lucky you have some kind of hint from the user. The gap between the raw material provided by the reflection API and picking applicable methods based on argument types has to be provided by something at the interpreter application level.
@Jason in an interpreter/dynamic language context, you (probably) don't have method overloading, so you can (probably) figure out which method should be called just by the name.
@Jason - my comment about needing to know the method a-priori is within the context of Java. Mentioning the reflection capabilities of an interpreter/dynamic language, though interesting in their own rights, are non sequitur to the context in which the OP's question was originally asked.
|
1

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.

1 Comment

Thanks, I did know about this but I didn't want to resort to it. I might have to, though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.