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包括原始类型、参数化类型、数组类型、类型变量和基本类型等

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/105985930
今日推荐