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,因为Localefinal

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,所以nullMethod.invoke()的第一个参数。第二个参数是要传递的参数数组。

$ java InvokeMain Deet Deet ja JP JP
invoking Deet.main()
invoking testDeet()
Locale = Japanese (Japan,JP), 
ISO Language Code = jpn
testDeet() returned true