深入分析JAVA 反射和泛型
從 JDK5 以后,Java 的 Class 類增加了泛型功能,從而允許使用泛型來限制 Class 類,例如,String.class 的類型實際上是 Class<String>。如果 Class 對應的類暫時未知,則使用 Class<?>。通過在反射中使用泛型,可以避兔使用反射生成的對象需要強制類型轉換。
泛型和 Class 類
使用 Class<T> 泛型可以避免強制類型轉換。例如,下面提供一個簡單的對象工廠,該對象工廠可以根據(jù)指定類來提供該類的實例。
public class CrazyitObjectFactory {
public static Object getInstance(String clsName) {
try {
// 創(chuàng)建指定類對應的Class對象
Class cls = Class.forName(clsName);
// 返回使用該Class對象所創(chuàng)建的實例
return cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
上面程序中兩行粗體字代碼根據(jù)指定的字符串類型創(chuàng)建了一個新對象,但這個對象的類型是 Object,因此當需要使用 CrazyitObjectFactory 的 getInstance() 方法來創(chuàng)建對象時,將會看到如下代碼:
// 獲取實例后需要強制類型轉換
Date d = (Date)Crazyit.getInstance("java.util.Date");
甚至出現(xiàn)如下代碼:
JFrame f = (JFrame)Crazyit.getInstance("java.util.Date");
上面代碼在編譯時不會有任何問題,但運行時將拋出 ClassCastException 異常,因為程序試圖將一個 Date 對象轉換成 JFrame 對象。
如果將上面的 CrazyitObjectFactory 工廠類改寫成使用泛型后的 Class,就可以避免這種情況。
public class CrazyitObjectFactory2 {
public static <T> T getInstance(Class<T> cls) {
try {
return cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
// 獲取實例后無須類型轉換
Date d = CrazyitObjectFactory2.getInstance(Date.class);
JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
}
}
在上面程序的 getInstance() 方法中傳入一個 Class<T> 參數(shù),這是一個泛型化的 Class 對象,調用該 Class 對象的 newInstance() 方法將返回一個 T 對象,如程序中粗體字代碼所示。接下來當使用 CrazyitObjectFactory2 工廠類的 getInstance() 方法來產(chǎn)生對象時,無須使用強制類型轉換,系統(tǒng)會執(zhí)行更嚴格的檢查,不會出現(xiàn) ClassCastException 運行時異常。
前面介紹使用 Array 類來創(chuàng)建數(shù)組時,曾經(jīng)看到如下代碼:
// 使用 Array 的 newInstance 方法來創(chuàng)建一個數(shù)組 Object arr = Array.newInstance(String.class, 10);
對于上面的代碼其實使用并不是非常方便,因為 newInstance() 方法返回的確實是一個 String[] 數(shù)組,而不是簡單的 Object 對象。如果需要將對象當成 String[] 數(shù)組使用,則必須使用強制類型轉換——這是不安全的操作。
為了示范泛型的優(yōu)勢,可以對 Array 的 newInstance() 方法進行包裝。
public class CrazyitArray {
// 對Array的newInstance方法進行包裝
@SuppressWarnings("unchecked")
public static <T> T[] newInstance(Class<T> componentType, int length) {
return (T[]) Array.newInstance(componentType, length); // ①
}
public static void main(String[] args) {
// 使用CrazyitArray的newInstance()創(chuàng)建一維數(shù)組
String[] arr = CrazyitArray.newInstance(String.class, 10);
// 使用CrazyitArray的newInstance()創(chuàng)建二維數(shù)組
// 在這種情況下,只要設置數(shù)組元素的類型是int[]即可。
int[][] intArr = CrazyitArray.newInstance(int[].class, 5);
arr[5] = "瘋狂Java講義";
// intArr是二維數(shù)組,初始化該數(shù)組的第二個數(shù)組元素
// 二維數(shù)組的元素必須是一維數(shù)組
intArr[1] = new int[] { 23, 12 };
System.out.println(arr[5]);
System.out.println(intArr[1][1]);
}
}
上面程序中粗體字代碼定義的 newInstance() 方法對 Array 類提供的 newInstance() 方法進行了包裝,將方法簽名改成了 public static <T> T[] newInstance(Class<T> componentType, int length),這就保證程序通過該 newInstance() 方法創(chuàng)建數(shù)組時的返回值就是數(shù)組對象,而不是 Object 對象,從而避免了強制類型轉換。
提示:程序在①行代碼處將會有一個 unchecked 編譯警告,所以程序使用了 @SuppressWarnings 來抑制這個警告信息。
使用反射來獲取泛型信息
通過指定類對應的 Class 對象,可以獲得該類里包含的所有成員變量,不管該成員變量是使用 private 修飾,還是使用 public 修飾。獲得了成員變量對應的 Field 對象后,就可以很容易地獲得該成員變量的數(shù)據(jù)類型,即使用如下代碼即可獲得指定成員變量的類型。
// 獲取成員變量 f 的類型 Class<?> a = f.getType();
但這種方式只對普通類型的成員變量有效。如果該成員變量的類型是有泛型類型的類型,如 Map<String, Integer> 類型,則不能誰確地得到該成員變量的泛型參數(shù)。
為了獲得指定成員變量的泛型類型,應先使用如下方法來獲取該成員變量的泛型類型。
// 獲得成員變量 f 的泛型類型 Type gType = f.getGenericType();
然后將 Type 對象強制類型轉換為 ParameterizedType 對象,ParameterizedType 代表被參數(shù)化的類型,也就是增加了泛型限制的類型。ParameterizedType 類提供了如下兩個方法。
- getRawType():返回沒有泛型信息的原始類型。
- getActualTypeArguments():返回泛型參數(shù)的類型。
下面是一個獲取泛型類型的完整程序。
public class GenericTest {
private Map<String, Integer> score;
public static void main(String[] args) throws Exception {
Class<GenericTest> clazz = GenericTest.class;
Field f = clazz.getDeclaredField("score");
// 直接使用getType()取出的類型只對普通類型的成員變量有效
Class<?> a = f.getType();
// 下面將看到僅輸出java.util.Map
System.out.println("score的類型是:" + a);
// 獲得成員變量f的泛型類型
Type gType = f.getGenericType();
// 如果gType類型是ParameterizedType對象
if (gType instanceof ParameterizedType) {
// 強制類型轉換
ParameterizedType pType = (ParameterizedType) gType;
// 獲取原始類型
Type rType = pType.getRawType();
System.out.println("原始類型是:" + rType);
// 取得泛型類型的泛型參數(shù)
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型信息是:");
for (int i = 0; i < tArgs.length; i++) {
System.out.println("第" + i + "個泛型類型是:" + tArgs[i]);
}
} else {
System.out.println("獲取泛型類型出錯!");
}
}
}
上面程序中的粗體字代碼就是取得泛型類型的關鍵代碼。運行上面程序,將看到如下運行結果:
score的類型是:interface java.util.Map
原始類型是:interface java.util.Map
泛型信息是:
第0個泛型類型是:class java.lang.String
第1個泛型類型是:class java.lang.Integer
從上面的運行結果可以看出,使用 getType() 方法只能獲取普通類型的成員變量的數(shù)據(jù)類型:對于增加了泛型的成員變量,應該使用 getGenericType() 方法來取得其類型。
提示:Type 也是 java.lang.reflect 包下的一個接口,該接口代表所有類型的公共高級接口,Class 是 Type 接口的實現(xiàn)類。Type 包括原始類型、參數(shù)化類型、數(shù)組類型、類型變量和基本類型等。
以上就是深入分析JAVA 反射和泛型的詳細內容,更多關于JAVA 反射和泛型的資料請關注腳本之家其它相關文章!
相關文章
Java開發(fā)中讀取XML與properties配置文件的方法
這篇文章主要介紹了Java開發(fā)中讀取XML與properties配置文件的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-01-01
MyBatis環(huán)境資源配置實現(xiàn)代碼詳解
這篇文章主要介紹了MyBatis環(huán)境資源配置實現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
Java并發(fā)編程之Semaphore(信號量)詳解及實例
這篇文章主要介紹了Java并發(fā)編程之Semaphore(信號量)詳解及實例的相關資料,需要的朋友可以參考下2017-06-06

