调用方法

原文: https://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html

Reflection 提供了一种在类上调用方法的方法。通常,只有在无法在非反射代码中将类的实例强制转换为所需类型时才需要这样做。用 java.lang.reflect.Method.invoke() 调用方法。第一个参数是要在其上调用此特定方法的对象实例。 (如果方法是static,则第一个参数应为null。)后续参数是方法的参数。如果底层方法抛出异常,它将被 java.lang.reflect.InvocationTargetException 包裹。可以使用异常链接机制的 InvocationTargetException.getCause() 方法检索方法的原始异常。

查找并调用具有特定声明的方法

考虑一个测试套件,它使用反射来调用给定类中的私有测试方法。 Deet 示例搜索以字符串“test”开头的类中的public方法,具有布尔返回类型和单个 Locale 参数。然后它调用每个匹配方法。

  1. import java.lang.reflect.InvocationTargetException;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Type;
  4. import java.util.Locale;
  5. import static java.lang.System.out;
  6. import static java.lang.System.err;
  7. public class Deet<T> {
  8. private boolean testDeet(Locale l) {
  9. // getISO3Language() may throw a MissingResourceException
  10. out.format("Locale = %s, ISO Language Code = %s%n", l.getDisplayName(), l.getISO3Language());
  11. return true;
  12. }
  13. private int testFoo(Locale l) { return 0; }
  14. private boolean testBar() { return true; }
  15. public static void main(String... args) {
  16. if (args.length != 4) {
  17. err.format("Usage: java Deet <classname> <langauge> <country> <variant>%n");
  18. return;
  19. }
  20. try {
  21. Class<?> c = Class.forName(args[0]);
  22. Object t = c.newInstance();
  23. Method[] allMethods = c.getDeclaredMethods();
  24. for (Method m : allMethods) {
  25. String mname = m.getName();
  26. if (!mname.startsWith("test")
  27. || (m.getGenericReturnType() != boolean.class)) {
  28. continue;
  29. }
  30. Type[] pType = m.getGenericParameterTypes();
  31. if ((pType.length != 1)
  32. || Locale.class.isAssignableFrom(pType[0].getClass())) {
  33. continue;
  34. }
  35. out.format("invoking %s()%n", mname);
  36. try {
  37. m.setAccessible(true);
  38. Object o = m.invoke(t, new Locale(args[1], args[2], args[3]));
  39. out.format("%s() returned %b%n", mname, (Boolean) o);
  40. // Handle any exceptions thrown by method to be invoked.
  41. } catch (InvocationTargetException x) {
  42. Throwable cause = x.getCause();
  43. err.format("invocation of %s failed: %s%n",
  44. mname, cause.getMessage());
  45. }
  46. }
  47. // production code should handle these exceptions more gracefully
  48. } catch (ClassNotFoundException x) {
  49. x.printStackTrace();
  50. } catch (InstantiationException x) {
  51. x.printStackTrace();
  52. } catch (IllegalAccessException x) {
  53. x.printStackTrace();
  54. }
  55. }
  56. }

Deet调用 getDeclaredMethods() ,它将返回类中显式声明的所有方法。此外, Class.isAssignableFrom() 用于确定定位方法的参数是否与期望的调用兼容。从技术上讲,代码可以测试以下语句是否是true,因为 Localefinal

  1. Locale.class == pType[0].getClass()

但是, Class.isAssignableFrom() 更为通用。

  1. $ java Deet Deet ja JP JP
  2. invoking testDeet()
  3. Locale = Japanese (Japan,JP),
  4. ISO Language Code = jpn
  5. testDeet() returned true
  1. $ java Deet Deet xx XX XX
  2. invoking testDeet()
  3. invocation of testDeet failed:
  4. Couldn't find 3-letter language code for xx

首先,请注意只有testDeet()符合代码强制执行的声明限制。接下来,当testDeet()传递无效参数时,它会抛出未选中的 java.util.MissingResourceException 。反思中,在处理已检查和未检查的异常方面没有区别。它们都包裹在 InvocationTargetException

调用具有可变数量的参数的方法

Method.invoke() 可用于将可变数量的参数传递给方法。要理解的关键概念是实现变量 arity 的方法,就好像变量参数被打包在一个数组中一样。

InvokeMain 示例说明了如何在任何类中调用main()入口点并传递在运行时确定的一组参数。

  1. import java.lang.reflect.InvocationTargetException;
  2. import java.lang.reflect.Method;
  3. import java.util.Arrays;
  4. public class InvokeMain {
  5. public static void main(String... args) {
  6. try {
  7. Class<?> c = Class.forName(args[0]);
  8. Class[] argTypes = new Class[] { String[].class };
  9. Method main = c.getDeclaredMethod("main", argTypes);
  10. String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);
  11. System.out.format("invoking %s.main()%n", c.getName());
  12. main.invoke(null, (Object)mainArgs);
  13. // production code should handle these exceptions more gracefully
  14. } catch (ClassNotFoundException x) {
  15. x.printStackTrace();
  16. } catch (NoSuchMethodException x) {
  17. x.printStackTrace();
  18. } catch (IllegalAccessException x) {
  19. x.printStackTrace();
  20. } catch (InvocationTargetException x) {
  21. x.printStackTrace();
  22. }
  23. }
  24. }

首先,为了找到main()方法,代码搜索名为“main”的类,其中一个参数是 String 的数组,因为main()staticnull ]是 Method.invoke() 的第一个参数。第二个参数是要传递的参数数组。

  1. $ java InvokeMain Deet Deet ja JP JP
  2. invoking Deet.main()
  3. invoking testDeet()
  4. Locale = Japanese (Japan,JP),
  5. ISO Language Code = jpn
  6. testDeet() returned true