Invoking Methods
反射提供了一种在类上调用方法的方法。通常,只有在无法通过非反射代码将类的实例转换为所需类型时,才需要这样做。用java.lang.reflect.Method.invoke()调用方法。第一个参数是要在其上调用此特定方法的对象实例。 (如果方法为static
,则第一个参数应为null
.)后续参数是方法的参数。如果基础方法引发异常,它将被java.lang.reflect.InvocationTargetExceptionpackage。可以使用异常链接机制的InvocationTargetException.getCause()方法检索该方法的原始异常。
查找和调用带有特定声明的方法
考虑一个使用反射在给定类中调用私有测试方法的测试套件。 Deet示例在类中搜索以字符串“ test
”开头,具有布尔返回类型和单个Locale参数的public
方法。然后,它调用每个匹配的方法。
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Locale;
import static java.lang.System.out;
import static java.lang.System.err;
public class Deet<T> {
private boolean testDeet(Locale l) {
// getISO3Language() may throw a MissingResourceException
out.format("Locale = %s, ISO Language Code = %s%n", l.getDisplayName(), l.getISO3Language());
return true;
}
private int testFoo(Locale l) { return 0; }
private boolean testBar() { return true; }
public static void main(String... args) {
if (args.length != 4) {
err.format("Usage: java Deet <classname> <langauge> <country> <variant>%n");
return;
}
try {
Class<?> c = Class.forName(args[0]);
Object t = c.newInstance();
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.startsWith("test")
|| (m.getGenericReturnType() != boolean.class)) {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if ((pType.length != 1)
|| Locale.class.isAssignableFrom(pType[0].getClass())) {
continue;
}
out.format("invoking %s()%n", mname);
try {
m.setAccessible(true);
Object o = m.invoke(t, new Locale(args[1], args[2], args[3]));
out.format("%s() returned %b%n", mname, (Boolean) o);
// Handle any exceptions thrown by method to be invoked.
} catch (InvocationTargetException x) {
Throwable cause = x.getCause();
err.format("invocation of %s failed: %s%n",
mname, cause.getMessage());
}
}
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
}
}
}
Deet
调用getDeclaredMethods(),这将返回该类中显式声明的所有方法。同样,Class.isAssignableFrom()用于确定所定位方法的参数是否与所需的调用兼容。从技术上讲,代码可以测试以下语句是否为true
,因为Locale是final
:
Locale.class == pType[0].getClass()
但是,Class.isAssignableFrom()更笼统。
$ java Deet Deet ja JP JP
invoking testDeet()
Locale = Japanese (Japan,JP),
ISO Language Code = jpn
testDeet() returned true
$ java Deet Deet xx XX XX
invoking testDeet()
invocation of testDeet failed:
Couldn't find 3-letter language code for xx
首先,请注意只有testDeet()
满足代码所强制的声明限制。接下来,当testDeet()
传递无效参数时,它将引发未经检查的java.util.MissingResourceException。反映出,对已检查的异常和未检查的异常的处理没有区别。它们都package在InvocationTargetException中
调用参数数目可变的方法
Method.invoke()可用于将可变数量的参数传递给方法。要理解的关键概念是,实现可变 Arity 的方法就像将可变参数包装在数组中一样。
InvokeMain示例说明了如何在任何类中调用main()
入口点并传递在运行时确定的一组参数。
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class InvokeMain {
public static void main(String... args) {
try {
Class<?> c = Class.forName(args[0]);
Class[] argTypes = new Class[] { String[].class };
Method main = c.getDeclaredMethod("main", argTypes);
String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);
System.out.format("invoking %s.main()%n", c.getName());
main.invoke(null, (Object)mainArgs);
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
} catch (NoSuchMethodException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (InvocationTargetException x) {
x.printStackTrace();
}
}
}
首先,要找到main()
方法,代码将搜索名称为“ main”的类,该类具有一个参数,该参数是String的数组。由于main()
是static
,所以null
是Method.invoke()的第一个参数。第二个参数是要传递的参数数组。
$ java InvokeMain Deet Deet ja JP JP
invoking Deet.main()
invoking testDeet()
Locale = Japanese (Japan,JP),
ISO Language Code = jpn
testDeet() returned true