JAVA泛型中的类型擦除及为什么不支持泛型数组
2016-04-09 16:27
323 查看
一,数组的协变性(covariant array type)及集合的非协变性
设有Circle类和Square类继承自Shape类。
关于数组的协变性,看代码:
如果给 totalArray(Shape[] arr) 传递一个Circle[] 类型的数组,这是可以的,编译通过,也能正常运行。也就是说:Circle[] IS-A Shape[]
关于集合的协变性,看代码:
如果给totalArea(Collection<Shape> arr)传递一个 Collection<Circle>类型的集合,这是不可以的。编译器就会报如下的错误:
也就是说:Collection<Circle> IS-NOT-A Collection<Shape>
二,如果解决集合的非协变性带来的不灵活?
出现了泛型!
这样,就可以给totalArea(Collection<? extends Shape> arr)
传递Collection<Circle>、Collection<Square>、Collection<Shape>类型的参数了。
三,泛型的类型擦除及类型擦除带来的ClassCastException异常
JAVA的泛型只存在于编译层,到了运行时,是看不到泛型的。
还是拿数组来做对比:
第5行代码在运行时会抛第4行中表示的异常。
再来看泛型:
编译器会对第6行提示第5行所示的警告。程序运行到第9行时抛出ClassCastException异常。因为ArrayList存储的本质上是一个Integer。
现在分析下第6行代码:
obj是Object类型的引用,strList是一个ArrayList<String>类型的引用,因此,向下转型时编译器给出了警告,在运行时,由于类型擦除,相当于
因此,代码运行到第6行也能通过。
对于第9行代码:
strList是一个ArrayList<String>类型的引用,当然可以调用 ArrayList的get方法。因此,编译时没问题。但在运行时,
由于,String str = strList.get(0);会编译成String str = (String)strList.get(0);
而strList.get(0)得到 的是一个Integer对象,然后把它赋值给 String str,由于Integer IS-NOT-A String。故抛出ClassCastException。
参考:
四,为什么不支持泛型数组
参考下面代码:
假设允许泛型数组,那么第2行是正确的,那么将不会有第1行中所示的编译错误。
那么就可以将 intArr 转型成 Object[],然后向Object[]放 ArrayList<String>,而不是我们想要的ArrayList<Integer>
因此,在运行时,类型是擦除的,运行时系统无法对数组中存储的类型做检查。它看到仅是:向intArr数组里面放 ArrayList对象。
相当于:
在上面第9行,如果改成:
我们以为Object o 它实际引用 的是Integer类型的,但它底层却是String类型的,如果调用 hashCode(),我们以为它执行的是Integer的hashCode(),但它执行的是String的hashCode(),那意味着发现不了错误。。。。。。。
因此,JAVA不支持泛型数组。
参考:
http://www.blogjava.net/deepnighttwo/articles/298426.html http://www.blogjava.net/sean/archive/2005/08/09/9630.html
设有Circle类和Square类继承自Shape类。
关于数组的协变性,看代码:
public static double totalArea(Shape[] arr){ double total = 0; for (Shape shape : arr) { if(shape != null) total += shape.area(); } return total; }
如果给 totalArray(Shape[] arr) 传递一个Circle[] 类型的数组,这是可以的,编译通过,也能正常运行。也就是说:Circle[] IS-A Shape[]
关于集合的协变性,看代码:
public static double totalArea(Collection<Shape> arr){ double total = 0; for (Shape shape : arr) { if(shape != null) total += shape.area(); } return total; }
如果给totalArea(Collection<Shape> arr)传递一个 Collection<Circle>类型的集合,这是不可以的。编译器就会报如下的错误:
The method totalArea(Collection<Shape>) in the type Demo is not applicable for the arguments (Collection<Circle>)
也就是说:Collection<Circle> IS-NOT-A Collection<Shape>
二,如果解决集合的非协变性带来的不灵活?
出现了泛型!
public static double totalArea(Collection<? extends Shape> arr){ double total = 0; for (Shape shape : arr) { if(shape != null) total += shape.area(); } return total; }
这样,就可以给totalArea(Collection<? extends Shape> arr)
传递Collection<Circle>、Collection<Square>、Collection<Shape>类型的参数了。
三,泛型的类型擦除及类型擦除带来的ClassCastException异常
JAVA的泛型只存在于编译层,到了运行时,是看不到泛型的。
还是拿数组来做对比:
String[] str = new String[10]; Object[] obj = str;//向上转型 //Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer obj[0] = new Integer(2);
第5行代码在运行时会抛第4行中表示的异常。
再来看泛型:
ArrayList<Integer> intList = new ArrayList<Integer>(); intList.add(2); Object obj = intList; //Type safety: Unchecked cast from Object to ArrayList<String> ArrayList<String> strList = (ArrayList<String>)obj; //Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String String str = strList.get(0); str.trim();//do something with str
编译器会对第6行提示第5行所示的警告。程序运行到第9行时抛出ClassCastException异常。因为ArrayList存储的本质上是一个Integer。
现在分析下第6行代码:
obj是Object类型的引用,strList是一个ArrayList<String>类型的引用,因此,向下转型时编译器给出了警告,在运行时,由于类型擦除,相当于
ArrayList strList = (ArrayList)obj;
因此,代码运行到第6行也能通过。
对于第9行代码:
strList是一个ArrayList<String>类型的引用,当然可以调用 ArrayList的get方法。因此,编译时没问题。但在运行时,
由于,String str = strList.get(0);会编译成String str = (String)strList.get(0);
而strList.get(0)得到 的是一个Integer对象,然后把它赋值给 String str,由于Integer IS-NOT-A String。故抛出ClassCastException。
参考:
四,为什么不支持泛型数组
参考下面代码:
//Cannot create a generic array of ArrayList<Integer> ArrayList<Integer>[] intArr = new ArrayList<Integer>[10]; Object[] obj = intArr; ArrayList<String> listStr = new ArrayList<String>(); obj[0] = listStr; ArrayList<Integer> listInt = intArr[0]; Integer i = listInt.get(0);//想要Integer,但却是String
假设允许泛型数组,那么第2行是正确的,那么将不会有第1行中所示的编译错误。
那么就可以将 intArr 转型成 Object[],然后向Object[]放 ArrayList<String>,而不是我们想要的ArrayList<Integer>
因此,在运行时,类型是擦除的,运行时系统无法对数组中存储的类型做检查。它看到仅是:向intArr数组里面放 ArrayList对象。
相当于:
// ArrayList<String> listStr = new ArrayList<String>(); ArrayList listStr = new ArrayList();//运行时看到的情况 // ArrayList<Integer> listInt = intArr[0]; ArrayList listInt = intArr[0];//运行时看到的情况
在上面第9行,如果改成:
Object o = listInt.get(0); //do something with o
我们以为Object o 它实际引用 的是Integer类型的,但它底层却是String类型的,如果调用 hashCode(),我们以为它执行的是Integer的hashCode(),但它执行的是String的hashCode(),那意味着发现不了错误。。。。。。。
因此,JAVA不支持泛型数组。
参考:
http://www.blogjava.net/deepnighttwo/articles/298426.html http://www.blogjava.net/sean/archive/2005/08/09/9630.html
相关文章推荐
- java学习系列----反射
- 【SVN】修改MyEclipse中的SVN地址
- 20145236 《Java程序设计》 第6周学习总结
- eclipse最有用快捷键整理
- 针对中科院java接口的使用方法和问题
- 比较字符串是否相等
- Java基础
- java--连接数据库--mysql--介绍
- java 递归算法
- Struts2源码分析(二)Struts2运行流程分析
- Java中super的几种用法并与this的区别
- struts整合之 spring 对于 destroy listen等问题
- java.net.BindException: Address already in use: JVM_Bind <null>:8001解决办法
- JAVA round 和 floor的区别
- 基于Spring3 MVC实现批量导出数据成Excel文件!
- 2.1 使用eclipse4.4 搭建 maven简单结构项目。
- Java获取月度篇(一)
- Struts2文件上传与过滤
- 快速排序_java实现
- java.lang.RuntimeException: Binary XML file line #52: You must supply a layout_height attribute.