泛型深入--1
泛型基础知识 泛型和编译器 参数化类型的特点
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. 泛型基础知识
1). 泛型术语
注意,这里面以ArrayList及其泛型为例
(1). 泛型类型
ArrayList<E>:泛型类型
E是类型变量的声明
(2). 类型参数/类型变量
E:称为类型参数或者类型变量
实际使用的时候,E要进行具体化
(3). 参数化 (后) 类型 ----Parameterized Type
整个ArrayList<Integer>称为类型化参数。
具体化的类型参数和容器类型组合到一起的整体类型叫做类型化类型。
(4). 原始类型 ----Raw Type
ArrayList本身称为原始类型。
就是没有引入类型参数的类型。
2). 参数化类型和原始类型的兼容性
(1). 参数化类型的引用可以指向一个原始类类型的对象
Collection<String> c =new Vector();
(2). 原始类类型的引用可以指向一个参数化类型的对象
Collection c2 =new Vector<String>();
参数化类型和原始类类型可以双向指向
【结论】无论是哪一种方式兼容,都是给Javac做编译检查的时候使用的。
由于javac编译之后类型擦除的存在,上面两种方式运行起来是一样的。
2. 泛型、编译器和字节码
泛型的应用场景:泛型常常用于集合和反射
泛型 编译器字节码文件
(1). 泛型出现的目的
简化了程序的书写 (不用强制类型转换) 和提高了数据的安全性 (将运行期间的类型转换错误移动到编译时期的编译错误)
(2). 泛型擦除
[1]. 擦除的概念:编译器对含有泛型的源文件编译完成之后,就对里面的存在泛型的地方进行类型擦除。
[2]. 擦除的结果:含有泛型信息的.java源文件对应的.class文件中不包含有任何泛型信息
字节码中对于原来存在泛型的地方,【全部是原始类型,没有任何的参数化类型】
e.g. 对于以下两行代码
ArrayList<String> collection1 =new ArrayList<String>();
ArrayList<Integer> collection2 =new ArrayList<Integer>();
{1}. 编译器知道collection1和collection2之间是有区别的。 collection1中的元素只能是String类行的,而collection2中的元素只能是Integer类行的。
但是编译器完成编译之后,ArrayList<String> collection1中的<String>和ArrayList<Integer> collection2中的<Integer>会被javac进行擦除!!!这样字节码中实际对应的代码是:
ArrayList collection1 =new ArrayList();
ArrayList collection2 =new ArrayList();
所以JVM运行起来的时候,对于collection1和collection2是没有任何类型上的差别。验证如下:
ArrayList<String> collection1 =new ArrayList<String>();
ArrayList<Integer> collection2 =new ArrayList<Integer>();
System.out.println(collection1.getClass().getName());
System.out.println(collection2.getClass().getName());
System.out.println(collection1.getClass()==collection2.getClass());
打印结果:
collection1和collection2的类型全部是 java.util.ArrayList 并且是对应的类型是一份字节码文件[collection1.getClass()==collection2.getClass()返回true]。
可以见到JVM运行时,没有遇到任何泛型类型参数信息。这足以证明编译器删掉了泛型信息。
(3). 函数重载和类型化参数
当一个类中的多个函数具有相同的参数个数:
[1]. 前面对应的参数类型均一致
[2]. 仅仅某一个参数是类型化参数的时候,这两个方法或者这几个方法的原始类型一样,仅仅是具体化的类型参数是不一致的
【结论】那么此时这几个函数是不能够构成重载的!!!
[3]. 原因分析 ----角度I
从擦除的角度分析:由于泛型的具体类型参数被具体化之后,javac仅仅在编译的时候帮助进行阻挡非法输入和省略强制类型转换之后,就会擦除掉参数化类型的具体化的类型参数。这个时候,ArrayList<String>,ArrayList<Integer>全都被擦除成了原始类型ArrayList。这样三个方法就是同名,同参数的三个方法,不是重载,是重复,所以不能存在于同一个类中。
[3]. 原因分析 ----角度II
反证法:假设这三个方法可以重载,编译通过。现在,在方法外部就可以调用这些方法了。假设: 调用语句是:
new TestV().printColl(new ArrayList(), 1);
由于 原始类型可以和 参数化类型进行 双向指向,那么 new ArrayList()既可以传给ArrayList<String>alS,也可以传给ArrayList<Integer>alT,还可以传给ArrayList al这三个参数所在的方法,到底调用哪个方法?javac没有办法搞定。所以编译报错。这样和前面的假设编译通过矛盾,所以就不能重载。----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------