类加载机制与反射6——反射和泛型

        从JDK5以后,Java的Class类增加了泛型功能,从而允许使用泛型来限制Class类,例如,String.class的类型实际上是Class<String>。如果Class对应的类暂时未知,则使用Class<?>。通过在反射中使用泛型,可以避免使用反射生成的对象需要强制类型转换。

  • 泛型和Class类
  • 使用反射来获取泛型信息

1.泛型和Class类

        使用Class<T>泛型可以避免强制类型转换。例如,下面提供了一个简单的对象工厂,该对象工厂可以根据指定类来提供该类的实例。

public class CrazyitObjectFactory
{
	public static Object getInstance(String clsName)
	{
		try
		{
			// 创建指定类对应的Class对象
			Class cls = Class.forName(clsName);
			// 返回使用该Class对象所创建的实例
			return cls.newInstance();
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}
}
        上面程序根据指定的字符串类型创建了一个新对象,但这个对象的类型是Object,因此当需要使用CarzyitObjectFactory的getInstance()方法来创建对象时,将会看到如下代码:
Date d = (Date)Crazyit.getInstance("java.util.Date");
甚至出现如下代码:
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>参数,这是一个泛型化的Class对象,调用该Class对象的newInstance()方法将返回一个T对象,如程序中粗体字代码所示。接下来当使用CrazyitObjectFactory2工厂类的getInstance()方法产生对象时,无须使用强制类型转换,系统会执行更严格的检查,不会出现ClassCastException运行时异常。

2.使用反射来获取泛型信息

        通过指定类对应的Class对象,可以获得该类里包含的所有成员变量,不管该成员变量是使用private修饰,还是使用public修饰。获得了成员变量对应的Field对象后,就可以很容易地获得该
成员变量的数据类型,即使用如下代码即可获得指定成员变量的类型
//获取成员变量f的类型
Class<?> a = f.getType();
但这种方式只对普通类型的成员变量有效。如果该成员变量的类型是有泛型类型的类型,如Map<String,Integer>类型,则不能准确地得到该成员变量的泛型参数。
为了获得指定成员变量的泛型类型,应先使用如下方法来获取该成员变量的泛型类型。
//获得成员变量f的泛型类型
Type gType = f.getGenericType();
然后将Type对象强制类型转换为ParameterizedTyp对象,ParameterizedType代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType类提供了如下两个方法:
①getRawType():返回没有泛型信息的原始类型;

②getActualTypeArguments():返回泛型参数的类型。

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);
			// 取得泛型类型的泛型参数
			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("获取泛型类型出错!");
		}
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值