简体   繁体   English

带有子类参数的Java getMethod

[英]Java getMethod with subclass parameter

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 .但是,当其中一个参数是该方法支持的类型之一的子类时,反射 API 将NoSuchMethodException For example, if the receiver's class has testMethod(Foo) defined, the following fails:例如,如果接收者的类定义了testMethod(Foo) ,则以下操作失败:

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

receiver.testMethod(new FooSubclass());
even though this works:

If your testMethod function has parameter of FooSuperClass type:如果您的testMethod函数具有FooSuperClass类型的参数:

 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 .然后,当您尝试使用反射获取匹配方法时: getClass().getMethod("testMethod", FooSubclass.class)将导致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:因为这个getMethod(String name, Class<?>... parameterTypes函数返回一个Method对象,它是一个具有给定name的公共成员方法,其中parameterTypes参数是标识方法形式参数类型的 Class 对象数组。实际上有没有用签名testMedthod(FooSubClass object)声明这样的方法,因为函数的FooSuperClass参类型是FooSuperClass 。所以,正确的调用是:

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

or , passing the super class by calling SubClass.class.getSuperClass() as follows:或者,通过调用SubClass.class.getSuperClass()传递超类,如下所示:

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:或者,将方法签名更改为: public void testMethod(FooSubClass object){}然后像现在一样调用:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM