10

我正在编写一个使用反射来动态查找和调用方法的库。只要给定一个对象、一个方法名和一个参数列表,我就需要调用给定的方法,就好像方法调用是在代码中显式编写的一样。

我一直在使用以下方法,在大多数情况下都有效:

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);
}

但是,当其中一个参数是该方法支持的类型之一的子类NoSuchMethodException时,反射 API 会抛出一个. 例如,如果接收者的类已testMethod(Foo)定义,则以下失败:

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

即使这有效:

receiver.testMethod(new FooSubclass());

我该如何解决这个问题?如果方法调用是硬编码的,则没有问题 - 编译器只是使用重载算法来选择要使用的最佳适用方法。但是,它不适用于反射,这正是我所需要的。

提前致谢!

4

2 回答 2

7

它比您开始时要长一点,但这可以满足您的要求......除此之外还有更多 - 例如, callMethod(receiver, "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();
    }
  }
}

于 2013-11-10T06:25:17.677 回答
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());
于 2013-11-10T06:52:00.377 回答