18.6 反射和泛型
18.6 反射和泛型
从JDK5
以后,Java
的Class
类增加了泛型功能,从而允许使用泛型来限制Class
类,例如, String.class
的类型实际上是Class<String>
。如果Class
对应的类暂时未知,则使用Classs<?>
。通过在反射中使用泛型,可以避免使用反射生成的对象需要强制类型转换。
18.6.1 泛型和Class类
使用Class<T>
泛型可以避免强制类型转换。
程序 简单对象工厂
例如,下面提供一个简单的对象工厂,该对象工厂可以根据指定类来提供该类的实例
1 | public class CrazyitObjectFactory { |
问题 需要强制类型转换
上面程序中try
块中的代码代码根据指定的字符串类型创建了一个新对象,但这个对象的类型是Object
,因此当需要使用CrazyitObjectFactory
的getInstance()
方法来创建对象时,将会看到如下代码
1 | //获取实例后需要强制类型转换 |
甚至出现如下代码:
1 | JFrame f=(JFrame )CrazyitObjectFactory.getInstance("java.util.Date"); |
上面代码在编译时不会有任何问题,但运行时将抛出ClassCastException
异常,因为程序试图将个Date
对象转换成JFrame
对象。
如果将上面的CrazyitobjectFactory
工厂类改写成使用泛型后的Class
,就可以避免这种情况。
程序 使用泛型后的对象工厂
1 |
|
在上面程序的getInstance
方法中传入一个Class<T>
参数,这是一个泛型化的Class
对象,调用该Class
对象的newInstance()
方法将返回一个T对象。接下来当使用CrazyitObjectFactory2
工厂类的getInstance()
方法来产生对象时,无须使用强制类型转换,系统会执行更严格的检查,不会出现ClassCastException
运行时异常.
Array类的创建数组方法(newInstance方法) 存在强制类型转换问题
前面介绍使用Array
类来创建数组时,曾经看到如下代码:
1 | //使用 Array的 newInstance方法来创建一个数组 |
对于上面的代码其实使用并不是非常方便,因为newInstance()
方法返回的确实是一个String
数组,而不是简单的Object
对象。如果需要将arr
对象当成String
数组使用,则必须使用强制类型转换,这是不安全的操作。
奇怪的是,Array
的newInstance()
方法签名为如下形式:
1 | static Object newInstance(Class<?> componentType, int length) |
在这个方法签名中使用了Class<?>
泛型,但并没有真正利用这个泛型;如果将该方法签名改为如下形式:
1 | public static <T> T[] newInstance(Class<T> componentType, int length); |
这样就可以在调用该方法后无须强制类型转换了
程序 包装Array类的newInstance方法解决强制类型转换问题
为了示范泛型的优势,可以对Array
的newInstance()
方法进行包装
1 | import java.lang.reflect.*; |
上面程序中粗体字代码定义的newInstance()
方法对Array
类提供的newInstance()
方法进行了包装,将方法签名改成了public static <T> T[] newInstance(Class<T> componentType, int length)
,
这就保证程序通过该newInstance()
方法创建数组时的返回值就是数组对象,而不是Object
对象,从而避免了强制类型转换。
程序在①行代码处将会有一个unchecked
编译警告,所以程序使用了@SuppressWarnings
来抑制这个警告信息。
18.6.2 使用反射来获取泛型信息
通过指定类对应的Class
对象,可以获得该类里包含的所有成员变量,不管该成员变量是使用private
修饰,还是使用public
修饰。获得了成员变量对应的Field
对象后,就可以很容易地获得该成员变量的数据类型,即使用如下代码即可获得指定成员变量的类型。
1 | //获取成员变量field的类型 |
但这种方式只对普通类型的成员变量有效。如果该成员变量的类型是有泛型类型的类型,如Map<String,Integer>
类型,则不能准确地得到该成员变量的泛型参数。
为了获得指定成员变量的泛型类型,应先使用如下方法来获取该成员变量的泛型类型
1 | //获得成员变量field的泛型类型 |
然后将Type
对象强制类型转换为ParameterizedType
对象,ParameterizedType
代表被参数化的类型,也就是增加了泛型限制的类型。
ParameterizedType
类方法
方法 | 描述 |
---|---|
Type[] getActualTypeArguments() |
返回泛型参数的类型 |
Type getRawType() |
返回没有泛型信息的原始类型。 |
Type getOwnerType() |
Returns a Type object representing the type that this type is a member of. |
程序 获取成员变量的类型的泛型信息
下面是一个获取泛型类型的完整程序。
1 | import java.util.*; |
运行上面程序,将看到如下运行结果:
1 | score的类型是:interface java.util.Map |
从上面的运行结果可以看出:
- 使用
getType()
方法只能获取普通类型的成员变量的数据类型; - 对于增加了泛型的成员变量,应该使用
getGenericType()
方法来取得其类型。
Type接口
Type
也是java.langreflect
包下的一个接口,该接口代表所有类型的公共高级接口,Class
是Type
接口的实现类。Type
包括原始类型、参数化类型、数组类型、类型变量和基本类型等。