Java面向對象系列[v1.0.0][反射與泛型]

泛型和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包括原始類型、參數化類型、數組類型、類型變量和基本類型等

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