深入分析JAVA 反射和泛型
从JDK5以后,Java的Class类增加了泛型功能,从而允许使用泛型来限制Class类,例如,String.class的类型实际上是Class
泛型和Class类
使用Class
publicclassCrazyitObjectFactory{ publicstaticObjectgetInstance(StringclsName){ try{ //创建指定类对应的Class对象 Classcls=Class.forName(clsName); //返回使用该Class对象所创建的实例 returncls.newInstance(); }catch(Exceptione){ e.printStackTrace(); returnnull; } } }
上面程序中两行粗体字代码根据指定的字符串类型创建了一个新对象,但这个对象的类型是Object,因此当需要使用CrazyitObjectFactory的getInstance()方法来创建对象时,将会看到如下代码:
//获取实例后需要强制类型转换 Dated=(Date)Crazyit.getInstance("java.util.Date");
甚至出现如下代码:
JFramef=(JFrame)Crazyit.getInstance("java.util.Date");
上面代码在编译时不会有任何问题,但运行时将抛出ClassCastException异常,因为程序试图将一个Date对象转换成JFrame对象。
如果将上面的CrazyitObjectFactory工厂类改写成使用泛型后的Class,就可以避免这种情况。
publicclassCrazyitObjectFactory2{ publicstaticTgetInstance(Class cls){ try{ returncls.newInstance(); }catch(Exceptione){ e.printStackTrace(); returnnull; } } publicstaticvoidmain(String[]args){ //获取实例后无须类型转换 Dated=CrazyitObjectFactory2.getInstance(Date.class); JFramef=CrazyitObjectFactory2.getInstance(JFrame.class); } }
在上面程序的getInstance()方法中传入一个Class
前面介绍使用Array类来创建数组时,曾经看到如下代码:
//使用Array的newInstance方法来创建一个数组 Objectarr=Array.newInstance(String.class,10);
对于上面的代码其实使用并不是非常方便,因为newInstance()方法返回的确实是一个String[]数组,而不是简单的Object对象。如果需要将对象当成String[]数组使用,则必须使用强制类型转换——这是不安全的操作。
为了示范泛型的优势,可以对Array的newInstance()方法进行包装。
publicclassCrazyitArray{ //对Array的newInstance方法进行包装 @SuppressWarnings("unchecked") publicstaticT[]newInstance(Class componentType,intlength){ return(T[])Array.newInstance(componentType,length);//① } publicstaticvoidmain(String[]args){ //使用CrazyitArray的newInstance()创建一维数组 String[]arr=CrazyitArray.newInstance(String.class,10); //使用CrazyitArray的newInstance()创建二维数组 //在这种情况下,只要设置数组元素的类型是int[]即可。 int[][]intArr=CrazyitArray.newInstance(int[].class,5); arr[5]="疯狂Java讲义"; //intArr是二维数组,初始化该数组的第二个数组元素 //二维数组的元素必须是一维数组 intArr[1]=newint[]{23,12}; System.out.println(arr[5]); System.out.println(intArr[1][1]); } }
上面程序中粗体字代码定义的newInstance()方法对Array类提供的newInstance()方法进行了包装,将方法签名改成了publicstatic
提示:程序在①行代码处将会有一个unchecked编译警告,所以程序使用了@SuppressWarnings来抑制这个警告信息。
使用反射来获取泛型信息
通过指定类对应的Class对象,可以获得该类里包含的所有成员变量,不管该成员变量是使用private修饰,还是使用public修饰。获得了成员变量对应的Field对象后,就可以很容易地获得该成员变量的数据类型,即使用如下代码即可获得指定成员变量的类型。
//获取成员变量f的类型 Class>a=f.getType();
但这种方式只对普通类型的成员变量有效。如果该成员变量的类型是有泛型类型的类型,如Map
为了获得指定成员变量的泛型类型,应先使用如下方法来获取该成员变量的泛型类型。
//获得成员变量f的泛型类型 TypegType=f.getGenericType();
然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType类提供了如下两个方法。
- getRawType():返回没有泛型信息的原始类型。
- getActualTypeArguments():返回泛型参数的类型。
下面是一个获取泛型类型的完整程序。
publicclassGenericTest{ privateMapscore; publicstaticvoidmain(String[]args)throwsException{ Class clazz=GenericTest.class; Fieldf=clazz.getDeclaredField("score"); //直接使用getType()取出的类型只对普通类型的成员变量有效 Class>a=f.getType(); //下面将看到仅输出java.util.Map System.out.println("score的类型是:"+a); //获得成员变量f的泛型类型 TypegType=f.getGenericType(); //如果gType类型是ParameterizedType对象 if(gTypeinstanceofParameterizedType){ //强制类型转换 ParameterizedTypepType=(ParameterizedType)gType; //获取原始类型 TyperType=pType.getRawType(); System.out.println("原始类型是:"+rType); //取得泛型类型的泛型参数 Type[]tArgs=pType.getActualTypeArguments(); System.out.println("泛型信息是:"); for(inti=0;i 上面程序中的粗体字代码就是取得泛型类型的关键代码。运行上面程序,将看到如下运行结果:
score的类型是:interfacejava.util.Map
原始类型是:interfacejava.util.Map
泛型信息是:
第0个泛型类型是:classjava.lang.String
第1个泛型类型是:classjava.lang.Integer从上面的运行结果可以看出,使用getType()方法只能获取普通类型的成员变量的数据类型:对于增加了泛型的成员变量,应该使用getGenericType()方法来取得其类型。
提示:Type也是java.lang.reflect包下的一个接口,该接口代表所有类型的公共高级接口,Class是Type接口的实现类。Type包括原始类型、参数化类型、数组类型、类型变量和基本类型等。
以上就是深入分析JAVA反射和泛型的详细内容,更多关于JAVA反射和泛型的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。