JAVA泛型中的类型擦除及为什么不支持泛型数组
一,数组的协变性(covariant array type)及集合的非协变性
设有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泛型中的类型擦除及为什么不支持泛型数组的更多相关文章
- Java泛型中的类型擦除机制简单理解
Java的泛型是JDK1.5时引入的.下面只是简单的介绍,不做深入的分析. Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...
- Java泛型-内部原理: 类型擦除以及类型擦除带来的问题
一:Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除.Java的泛型基本上都是在编译 ...
- java为什么要用类型擦除实现泛型?--c++,java,c# 的泛型是如何实现的
所以总结一下c++,java,c#的泛型.c++的泛型在编译时完全展开,类型精度高,共享代码差.java的泛型使用类型擦出,仅在编译时做类型检查,在运行时擦出,共享代码好,但是类型精度不行.c#的泛型 ...
- c++中的类型擦除
(原创)c++中的类型擦除 c++11 boost技术交流群:296561497,欢迎大家来交流技术. 关于类型擦除,可能很多人都不清楚,不知道类型擦除是干啥的,为什么需要类型擦除.有必要做个说明,类 ...
- 详解Java 8中Stream类型的“懒”加载
在进入正题之前,我们需要先引入Java 8中Stream类型的两个很重要的操作: 中间和终结操作(Intermediate and Terminal Operation) Stream类型有两种类型的 ...
- Java中的类型擦除与桥方法
类型擦除 Java在语法中虽然存在泛型的概念,但是在虚拟机中却没有泛型的概念,虚拟机中所有的类型都是普通类.无论何时定义一个泛型类型,编译后类型会被都被自动转换成一个相应的原始类型. 比如这个类 pu ...
- java泛型总结(类型擦除、伪泛型、陷阱)
JDK1.5开始实现了对泛型的支持,但是java对泛型支持的底层实现采用的是类型擦除的方式,这是一种伪泛型.这种实现方式虽然可用但有其缺陷. <Thinking in Java>的作者 B ...
- Java进阶(四)Java反射TypeToken解决泛型运行时类型擦除问题
在开发时,遇到了下面这条语句,不懂,然习之. private List<MyZhuiHaoDetailModel> listLottery = new ArrayList<MyZhu ...
- (原创)c++中的类型擦除
c++11 boost技术交流群:296561497,欢迎大家来交流技术. 关于类型擦除,可能很多人都不清楚,不知道类型擦除是干啥的,为什么需要类型擦除.有必要做个说明,类型擦除就是将原有类型消除或者 ...
随机推荐
- SAP PA认证
----------------------------------------------------------------转帖---------------------------------- ...
- mysql的主主复制详解
Mysql双主部署 解释: 所谓双主备份,其实也就是互做主从复制,每台master既是master,又是另一台服务器的slave.这样,任何一方所做的变更,都会通过复制应用到另外一方的数据库中. 要求 ...
- JIRA & GitHub
JIRA & GitHub https://confluence.atlassian.com/adminjiracloud/connect-jira-cloud-to-github-81418 ...
- jvm学习二:类加载器
前一节详细的聊了一下类的加载过程,本节聊一聊类的加载工具,类加载器 --- ClassLoader 本想自己写的,查资料的时候查到一篇大神的文章,写的十分详细 大家直接过去看吧http://blo ...
- BZOJ4699 树上的最短路(最短路径+dfs序+线段树+堆+并查集)
首先一般化的将下水道和塌陷看成一个东西.注意到在从源点出发的所有需要使用某条下水道的最短路径中,该下水道只会被使用一次,该下水道第一个被访问的点相同,且只会在第一个访问的点使用该下水道.这个第一个访问 ...
- matplotlib极坐标方法详解
一.极坐标 在平面内取一个定点O,叫极点,引一条射线Ox,叫做极轴,再选定一个长度单位和角度的正方向(通常取逆时针方向).对于平面内任何一点M,用ρ表示线段OM的长度(有时也用r表示),θ表示从Ox到 ...
- MT【238】内心轨迹
已知$F_1,F_2$为椭圆$C:\dfrac{x^2}{4}+\dfrac{y^2}{3}=1$的左右焦点,点$P$在椭圆$C$上移动时,$\Delta{F_1PF_2}$的内心$I$的轨迹方程为_ ...
- Leetcode 414.Fizz Buzz By Python
写一个程序,输出从 1 到 n 数字的字符串表示. 如果 n 是3的倍数,输出"Fizz": 如果 n 是5的倍数,输出"Buzz": 3.如果 n 同时是3和 ...
- emwin之LISTWHEEL控件之LISTWHEEL_SetPos函数的使用
@2018-09-05 [小记] LISTWHEEL控件的LISTWHEEL_SetPos函数的使用注意事项: 该函数不能在创建窗口的WM_INIT_DIALOG分支下作为修改LISTWHEEL控件属 ...
- 洛谷 P1450.硬币购物 解题报告
P1450.硬币购物 题目描述 硬币购物一共有\(4\)种硬币.面值分别为\(c1,c2,c3,c4\).某人去商店买东西,去了\(tot\)次.每次带\(d_i\)枚\(c_i\)硬币,买\(s_i ...