10

I'm writing a library that uses reflection to find and call methods dynamically. Given just an object, a method name, and a parameter list, I need to call the given method as though the method call were explicitly written in the code.

I've been using the following approach, which works in most cases:

static void callMethod(Object receiver, String methodName, Object[] params) {
    Class<?>[] paramTypes = new Class<?>[params.length];
    for (int i = 0; i < param.length; i++) {
        paramTypes[i] = params[i].getClass();
    }
    receiver.getClass().getMethod(methodName, paramTypes).invoke(receiver, params);
}

However, when one of the parameters is a subclass of one of the supported types for the method, the reflection API throws a NoSuchMethodException. For example, if the receiver's class has testMethod(Foo) defined, the following fails:

receiver.getClass().getMethod("testMethod", FooSubclass.class).invoke(receiver, new FooSubclass());

even though this works:

receiver.testMethod(new FooSubclass());

How do I resolve this? If the method call is hard-coded there's no issue - the compiler just uses the overloading algorithm to pick the best applicable method to use. It doesn't work with reflection, though, which is what I need.

Thanks in advance!

4
  • Could you provide an SSCCE? Commented Nov 10, 2013 at 4:55
  • The issue is with polymorphism, rather than overloading. See: stackoverflow.com/questions/3083786/… Commented Nov 10, 2013 at 5:02
  • you can't invoke a function with reflection without specifying formal parameter type Commented Nov 10, 2013 at 6:53
  • Possible duplicate of Java getMethod with superclass parameters in method Commented May 26, 2017 at 15:30

2 Answers 2

8

It's a bit longer than what you started with, but this does what you asked for... and a little more besides - for example, callMethod(receiver, "voidMethod") where voidMethod takes no arguments also works.

static void callMethod(Object receiver,
      String methodName, Object... params) {
  if (receiver == null || methodName == null) {
    return;
  }
  Class<?> cls = receiver.getClass();
  Method[] methods = cls.getMethods();
  Method toInvoke = null;
  methodLoop: for (Method method : methods) {
    if (!methodName.equals(method.getName())) {
      continue;
    }
    Class<?>[] paramTypes = method.getParameterTypes();
    if (params == null && paramTypes == null) {
      toInvoke = method;
      break;
    } else if (params == null || paramTypes == null
        || paramTypes.length != params.length) {
      continue;
    }

    for (int i = 0; i < params.length; ++i) {
      if (!paramTypes[i].isAssignableFrom(params[i].getClass())) {
        continue methodLoop;
      }
    }
    toInvoke = method;
  }
  if (toInvoke != null) {
    try {
      toInvoke.invoke(receiver, params);
    } catch (Exception t) {
      t.printStackTrace();
    }
  }
}

Sign up to request clarification or add additional context in comments.

2 Comments

This would look much better if you splitted the part that find the correct method (since that's what doesn't work correctly with subclasses) and the invocation of the method itself (that works flawlessly once you've got the right Method).
This is almost too genius, know what I mean? It worked though, when nothing else did, so the example was very appreciated.
1
receiver.testMethod(new FooSubclass());
even though this works:

If your testMethod function has parameter of FooSuperClass type:

 public void testMethod(FooSuperClass object){}

then, while you are trying to get a matching method with reflection: getClass().getMethod("testMethod", FooSubclass.class) will result in NoSuchMethodException. Because this getMethod(String name, Class<?>... parameterTypes function returns a Method object which is a public member method with the given name where parameterTypes parameter is an array of Class objects that identify the method's formal parameter types. There is actually no such method is declared with signature testMedthod(FooSubClass object) as the formal parameter type of the function is FooSuperClass. So, the correct invocation is:

receiver.getClass().getMethod("testMethod", FooSuperClass.class)
                        .invoke(receiver, new FooSubclass());

or, passing the super class by calling SubClass.class.getSuperClass() as follows:

receiver.getClass().getMethod("testMethod", FooSubClass.class.getSuperclass())
                            .invoke(receiver, new FooSubclass());

or, changing the method signature to: public void testMethod(FooSubClass object){} and then invoke as you are doing now:

receiver.getClass().getMethod("testMethod", FooSubclass.class)
                         .invoke(receiver, new FooSubclass());

Comments

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.