泛型和Class類
使用Class泛型可以避免強制類型轉換,如下代碼是一個簡單的對象工廠,該對象工廠可以根據指定類來提供該類的實例
public class CrazyitObjectFactory
{
public static Object getInstance(String clsName)
{
try
{
// 創建指定類對應的Class對象
Class cls = Class.forName(clsName);
// 返回使用該Class對象所創建的實例
return cls.getConstructor().newInstance();
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
}
當使用這個類創建對象的時候,會是
// 獲取實例後需要強制類型轉換
Date d = (Date)Crazyit.getInstance("java.util.Date");
JFrame f = (JFrame)Crazyit.getInstance("java.util.Date");
然而在編譯時不會有任何問題,但運行時會拋出ClassCastException,因爲程序試圖講一個Date對象轉換成JFrame對象,優化代碼改寫成使用泛型後的Class
import java.util.*;
import javax.swing.*;
public class CrazyitObjectFactory2
{
public static <T> T getInstance(Class<T> cls)
{
try
{
return cls.getConstructor().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參數,這是一個泛型化的Class對象,調用該Class對象的newInstance()方法將返回一個T對象,在使用這個工廠類的getInstance()方法來產生對象時,無需使用強制類型轉換,不會出現ClassCastException運行時異常
泛型和數組
// 使用Array的newInstance方法來創建一個數組
Object array1 = Array.newInstance(String.class, 10);
代碼中newInstance()方法返回的確實是一個String[]數組,而不是簡單的Object對象,如果需要將array1當成String[]數組使用,則必須使用強制類型轉換,奇怪的是,Array的newInstance()方法的方法簽名是:
public static Object newInstance(Class<?> componentType, int... dimensions)
,在這個方法簽名中使用了Class<?>泛型,但並沒有真正利用這個泛型,如果將該方法改成public static <T> T[] newInstance(Class<T> componentType, int length)
這樣就可以在調用該方法後無需進行強制類型轉換了,但它只支持一維數組的創建
import java.lang.reflect.*;
import java.lang.annotation.*;
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()創建一維數組
String[] arr = CrazyitArray.newInstance(String.class, 10);
// 使用CrazyitArray的newInstance()創建二維數組
// 在這種情況下,只要設置數組元素的類型是int[]即可。
int[][] intArr = CrazyitArray.newInstance(int[].class, 5);
arr[5] = "我自橫刀向天笑";
// intArr是二維數組,初始化該數組的第二個數組元素
// 二維數組的元素必須是一維數組
intArr[1] = new int[] {23, 12};
System.out.println(arr[5]);
System.out.println(intArr[1][1]);
}
}
使用反射獲取泛型信息
通過指定類對應的Class對象,可以獲得該類裏包含的所有成員變量,不管該成員變量是使用private修飾,還是使用public修飾,獲得了成員變量對應的Field對象後,就可以獲得該成員變量的數據類型
// 獲得成員變量f的類型
Class<?> a = f.getType()
但這種方式只對普通類型的成員變量有效,如果該成員變量的類型是有泛型類型的類型,例如Map<String, Integer>類型,應使用如下方法來獲得該成員變量的泛型類型
// 獲得成員變量的f的泛型類型
Type gType = f.getGenericType();
然後將Type對象強制類型轉換爲ParameterizedType對象,ParameterizedType代表被參數化的類型,也就是增強了泛型限制的類型,ParameterizedType提供了兩個方法:
- getRawType():返回沒有泛型信息的原始類型
- getActualTypeArguments():返回泛型參數的類型
import java.util.*;
import java.lang.reflect.*;
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)
{
// 強制類型轉換
var pType = (ParameterizedType) gType;
// 獲取原始類型
Type rType = pType.getRawType();
System.out.println("原始類型是:" + rType);
// 取得泛型類型的泛型參數
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型信息是:");
for (var i = 0; i < tArgs.length; i++)
{
System.out.println("第" + i + "個泛型類型是:" + tArgs[i]);
}
}
else
{
System.out.println("獲取泛型類型出錯!");
}
}
}
執行結果爲:
D:\CrazyJava\CrazyJava\codes\18\18.6>java GenericTest
score的類型是:interface java.util.Map
原始類型是:interface java.util.Map
泛型信息是:
第0個泛型類型是:class java.lang.String
第1個泛型類型是:class java.lang.Integer
Type也是java.lang.reflect包下的一個接口,該接口代表所有類型的公共高級接口,Class是Type接口的實現類,Type包括原始類型、參數化類型、數組類型、類型變量和基本類型等