我正在编写一个使用反射来动态查找和调用方法的库。只给出一个对象,一个方法名和一个参数列表,我需要调用给定的方法,就好像方法调用是在代码中显式编写的一样。
我一直在使用以下方法,在大多数情况下都适用:
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);
}
但是,当其中一个参数是该方法支持的某个类型的子类时,反射API会抛出NoSuchMethodException
。例如,如果接收者的类定义了testMethod(Foo)
,则以下操作失败:
receiver.getClass().getMethod("testMethod", FooSubclass.class).invoke(receiver, new FooSubclass());
即使这样有效:
receiver.testMethod(new FooSubclass());
我该如何解决这个问题?如果方法调用是硬编码的,则没有问题 - 编译器只使用重载算法来选择最适用的方法。但它不适用于反射,这就是我所需要的。
提前致谢!
答案 0 :(得分:7)
这比你开始的时间要长一点,但这样做你要求的......还有一点 - 例如,callMethod(接收器,“voidMethod”),其中voidMethod不带参数也可以。
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();
}
}
}
答案 1 :(得分:1)
receiver.testMethod(new FooSubclass());
even though this works:
如果您的testMethod
函数的参数为FooSuperClass
类型:
public void testMethod(FooSuperClass object){}
然后,当您尝试使用反射获得匹配的方法时:getClass().getMethod("testMethod", FooSubclass.class)
将导致NoSuchMethodException
。因为此getMethod(String name, Class<?>... parameterTypes
函数返回Method
对象,该对象是具有给定name
的公共成员方法,其中parameterTypes
参数是一个Class对象数组,用于标识方法的 形式参数类型 。实际上没有使用签名testMedthod(FooSubClass object)
声明此方法,因为函数的形式参数类型是FooSuperClass
。所以,正确的调用是:
receiver.getClass().getMethod("testMethod", FooSuperClass.class)
.invoke(receiver, new FooSubclass());
或,通过调用SubClass.class.getSuperClass()
传递超类,如下所示:
receiver.getClass().getMethod("testMethod", FooSubClass.class.getSuperclass())
.invoke(receiver, new FooSubclass());
或,将方法签名更改为:public void testMethod(FooSubClass object){}
,然后按照您现在的步骤进行调用:
receiver.getClass().getMethod("testMethod", FooSubclass.class)
.invoke(receiver, new FooSubclass());