三、总结:
1、查看ArrayList的API文档,你就可以发现,其中的ArrayList是这样定义的:ArrayList<E>,而其中的add方法定义为:boolean add(E e),你是不是发现了什么。
我个人认为,虽然反射将类型擦出了,其实反射是脆弱的;即使擦出了集合的类型信息,但是其中的方法可能还会保留原始限定的类型。这里虽然定义了E,要和上面的一样,但是在使用反射的时候,传入的类型参数必须是Object.class。如果不是的话,虽然编译时期并没有问题,但是运行时期是会报错的。
2、对于返回类型和集合上限定的类型有关的,即在API中定义的返回类型为E的;如果只是通过反射加入了非限定类型的数据,直接调用这个方法的,并不用强转;而如果调用了这个方法有调用了其他方法,如list.get(0).getClass(),那么需要将list.get(0)作为一个整体强转为Object,即将数据封装到Object中,才能调用其他方法。
3、对于返回类型与集合无关的,如size这个方法,无需强转,直接获取。因为size()的返回类型是int,并不是定义的E。
4、很重要的一点:除了String类型以外的其他类型(包括自定义类型),无需强转,可以直接使用相应集合中的方法。如上面的get()方法。我个人理解,与集合有关的方法,如get方法只有在你添加进元素时,才能调用,所以,即使get定义为:E get(int index),也无需强转。这里,String类型除外。
4、但是对于String类型呢?作为String类型的值,如果添加入限定为非String集合中,那么就需要强转,究其原因,我只能说String是比较特殊的一个类,它有如下特性:被定义为final,而且其值是存在了常量池中,并且可以将任意类型值通过字符串打印。基于如此特殊的类,那么,它必然也有某些特性。所以,我个人认为:在运行时是会检查的,并不是将数据封装到了Object中,而是会检查像get()这样的方法本身的类型和传入集合中的数据的类型的。
四、收获
通过这个测试,我自己给反射的作用起了两个名字:“自动包装”和“屏蔽类型”。也就是说反射出来的成员,都是作为Object返回的,这就屏蔽了成员本身的类型,从而切断了和其中数据的关联,也不是说分隔了成员类型和其中的数据类型。如上面说的get()本身返回的是E这个类型,而获取到的是非法传入的其他类型。
从中,我也看到了反射的脆弱,并不是随便通过反射传入其他值,就能成功通过运行的,在某种程度上,反射并不能做到完全的屏蔽,对于String如此特殊的类型,反射就显得有些脆弱了。所以在使用反射的时候,需要特别注意,如果不是用在框架中,尽量不用反射。另一方面,我也认识到了String类型的特殊之处,String中的很多方法都重写了Object中的方法,是不是可以从这里找到String的特殊之处的答案呢?有待研究。
后续:
大家也可以自己尝试着将集合的限定类型设定为其他类型,如Person类等,那么你会发现一些规律的。
|