【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對象的回調功能。使用接口進行回調會使得代碼的執行速度更快,更易於維護。

捐贈

若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。

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