Java使用子类参数的getMethod方法

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

然而,当其中一个参数是该方法支持类型的子类时,反射 API 会抛出 NoSuchMethodException 异常。例如,如果接收者的类定义了 testMethod(Foo),以下示例将失败:

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

尽管这个有效:

receiver.testMethod(new FooSubclass());

如何解决这个问题?如果方法调用是硬编码的,那么没有问题——编译器只使用重载算法来选择最适用的方法。但是,使用反射就行不通了,而这正是我需要的。

提前致谢!


你能提供一个SSCCE吗? - axiopisty
问题出在多态上,而不是重载。请参见:https://dev59.com/_07Sa4cB1Zd3GeqP4pAl - jaco0646
在反射中调用函数时,必须指定形式参数类型。 - Sage
可能是Java getMethod with superclass parameters in method的重复问题。 - Daniel Bickler
2个回答

7

这段代码比你原来的代码长一些,但是它可以实现你要求的功能...并且还能额外做一些事情 - 例如,当voidMethod不需要参数时,调用callMethod(receiver, "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();
    }
  }
}


如果你将寻找正确方法的部分(因为这是在子类中无法正常工作的部分)和调用方法本身的部分(一旦你找到了正确的方法,它就能完美运行)分开,效果会好得多。 - A. Rama
这几乎太天才了,你知道我的意思吗?虽然其他方法都不行,但它确实有效,所以这个例子非常受欢迎。 - J. M. Becker

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接