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!
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();
}
}
}
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());
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.