Java泛型
一、概述
泛型程序设计是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
- 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。
- 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。
不管是类还是参数,泛型的参数在真正使用泛型时都必须作出指明。
Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
但他并没有完全真正的实现泛型,而是通过一些内部处理来使其看起来像泛型,这也是为什么有人称之为伪泛型的原因。
二、类型擦除
如果研究过其他泛型程序设计语言(如:C#、.NET),会发现Java泛型只体现在源码层面,在编译后的字节码文件中,就被替换成了原始类型。并且在相应的地方加以转换处理,因此对于运行时的Java来说ArrayList<String>
和ArrayList<Integer>
是同一个类。这实际就是是一种语法糖,而非真正实现泛型。
在编译期间将所有泛型信息(类型变量、参数化类型)替换为原始类型的过程就是类型擦除。
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("abc");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(123);
System.out.println(list1.getClass() == list2.getClass());
}
这段代码最终会输出true,两个不同泛型类型的对象的类(字节码)是一样的。说明泛型类型String
和Integer
都被擦除掉了,只剩下原始类型。
2.1、类型擦除带来的问题以及解决方法
使用类型擦除的方式会导致与原来的语言设计上出现一些问题,当然Java设计者已经在里面进行了处理。
2.1.1、在类型擦除后如何保证只能使用泛型变量限定的类型呢?
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(123);
list1.add("123"); //编译异常
ArrayList list2 = new ArrayList<Integer>();
list1.add(123);
list1.add("123"); //编译正常
}
如上,类型擦除后,类型应该是原始类型Object了,也就是说可操作任何Java类型,但显然并不行,它只能操作泛型变量限定的类型的元素。
这就是设计者的解决方法:类型检查
在编译前,先进行类型检查,new ArrayList<>
只是开辟一个内存空间,可以存储任何类型的数据对象,而类型检查是针对它的引用,这个引用list1
调用泛型方法时,就会进行类型检查,而无关于它真正引用的对象new ArrayList<Integer>()
。
2.1.2、自动类型转换
经过类型擦除后,字节码没有了任何泛型类型的信息,类型都是Object,那么在后续存取时是否需要通过强转才能获得泛型类型的数据呢?
//ArrayList.get(int index);
E elementData(int index) {
return (E) elementData[index];
}
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
对此,设计者也在里面进行转换,而不需要开发者每次都做转换操作。
类型擦除还有很多类似的问题,而设计者也对应的进行了处理,有兴趣的可深入了解:类型擦除后与多态的冲突、在静态方法\类的问题、运行时类型查询等问题。
三、泛型中E、T、K、V、N、?、Object等通配符的含义
-
E: Element (在集合中使用,因为集合中存放的是元素)
-
T: Type(Java 类型)
-
K: Key(键)
-
V: Value(值)
-
N: Number(数值类型)【Short、Integer、Float、Double、Number、Long…】
-
?: 不确定的java类型(非限定通配符)
-
Object: 所有类的根类,任何类型的对象都可以设置给改Object引用变量,使用的时候可能需要进行类型转换
四、限定通配符和非限定通配符
限定通配符就是对泛型类型进行了限制,而限定通配符可以限定泛型类型的上界与下界:
- 限定上界:<? extends T> 表示泛型类型必须是T类型或者是T的子类
- 限定下界:<? super T> 表示泛型类型必须是T类型或者是T的父类
如果泛型类型在限定界限外将会导致编译时错误。
非限定通配符<?> 表示泛型类型可以是任何类型。
五、List<Object>
和原始类型List
以及List<?>
之间的区别
List | List<Objec> | List<?> | |
---|---|---|---|
类型 | 原始类型 | 实际类型参数为Object的参数化类型 | 通配符类型 |
泛型类型 | 任何对应List<E> 的参数化类型 | 仅接受List和其本身类型 | 任何对应List<E> 的参数化类型,包括List |
元素类型 | 可以操作任意类型的元素 | 可以添加任何元素 | 不能添加任何元素 |
安全性 | 不安全 | 安全性 | 安全性 |
便利性 | 不便利性 | 便利性 | 不便利性 |
表述性 | 不表述性 | 表述性 | 表述性 |