【java基础(三十四)】反射(四)

调用任意方法

在C和C++中,可以从函数指针执行任意函数。从表面看,Java没有提供方法指针,即将一个方法的存储地址传给另一个方法,以便第二个方法能够随后调用它。事实上,Java的设计者曾说过:方法指针是很危险的,并且常常带来隐患。他们认为Java提供的接口(interface)是一种更好的解决方法。然而,反射机制允许你调用任意方法。

为了能够看到方法指针的工作过程,先回一下利用Field类的get方法查看对象域的过程。与之类似,在Method类有一个invoke方法,它允许调用包装在当前Method对象中的方法。invoke方法的签名是:

Object invoke(Object obj, Object... args)

第一个参数是隐式参数,其余的对象提供了显示参数。

对于静态方法,第一个参数可以被忽略,即可以将它设置为null。

例如,假设用m1代表Employee类的getName方法,下面这条语句显示了如何调用这个方法:

String n = (String)m1.invoke(harry);

如果返回类型是基本类型,invoke方法会返回其包装器类型。如,假设m2表示Employee类的getSalary方法,那么返回的对象实际上是一个Double,必须相应地完成类型转换。可以使用自动拆箱将它转换为一个double:

double s = (Double)m2.invoke(harry);

如何得到Method对象呢?当然,可以通过调用getDeclareMethods方法,然后对返回的Method对象数组进行查找,知道发现想要的方法为止。也可以通过调用Class类中的getMethod方法得到想要的方法。它与getField方法类似。getField方法根据表示域名的字符串,返回一个Field对象,然而,有可能存在若干个相同名字的方法,因此要格外小心,以确保能够准确地得到想要的那个方法。有鉴于此,还必须提供想要的方法的参数类型。getMethod的签名是:

Method getMethod(String name, Class... parameterType)

如:下面例子是获得Employee类的getName方法和raiseSalary方法的方法指针。

Method m1 = Employee.class.getMethod("getName");
Method m2 = Employee.class.getMethod("raiseSalay", double.class);

实例

package cn.freedompc.methods;

import java.lang.reflect.Method;

public class MethodTableTest {
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		Method square = MethodTableTest.class.getMethod("square", double.class);
		Method sqrt = Math.class.getMethod("sqrt", double.class);

		printTable(1, 10, 10, square);
		printTable(1, 10, 10, sqrt);
	}
	
	public static double square(double x) {
		return x * x;
	}
	
	public static void printTable(double from, double to, int n, Method f) {
		System.out.println(f);
		double dx = (to - from) / (n - 1);
		for (double x = from; x <= to; x += dx) {
			try {
				double y = (Double) f.invoke(null, x);
				System.out.printf("%10.4f | %10.4f%n", x, y);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

结果:
在这里插入图片描述

分析

上面的程序清楚的表明,可以使用method对象实现C语言中的函数指针的所有操作。同C一样,这种程序设计风格并不太简便,出错的可能性也比较大。如果在调用方法的时候提供了一个错误的参数,那么invoke方法将会抛出一个异常。

另外,invoke的参数和返回值必须的Object类型的。这就意味着必须进行多次的类型转换。这样做将会使编译器错过检查代码的机会。因此,等到测试阶段才会发现这些错误,找到并改正它们将会更加困难。不仅如此,使用反射获得方法指针的代码要比仅仅直接调用方法明显慢一些。

有鉴于此,建议仅在必要的时候才使用Method对象,而最好使用接口以及Java SE 8中的lambda表达式。特别要重申:建议Java开发者不要使用Method对象的回调功能。使用接口进行回调会使得代码的执行速度更快,更易于维护。

捐赠

若你感觉读到这篇文章对你有启发,能引起你的思考。请不要吝啬你的钱包,你的任何打赏或者捐赠都是对我莫大的鼓励。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章