Java泛型
泛型的意思就是参数化类型。在没有引入泛型机制前,需要针对不同的数据类型重复编写相同的代码,而引入泛型后就可以将数据类型与代码逻辑分离开,提高了程序的简洁性和可读性,同时也提供了编译时的类型转换安全检测功能。
泛型可以将类型参数化,参数一旦确定,如果类型不匹配,就无法通过编译。
泛型的使用
泛型的使用可以分为:
- 泛型类
- 泛型方法
- 泛型接口
通配符
对于任意一个类型,可以用字母来表示泛型类型,如List<T>
,这种是确定的类型,而使用通配符<?>
则可以指定泛型中的类型范围。
<?>
是无限定的通配符;<? extends A>
是有上界的通配符,限定类A
和A
的子类;<? super A>
是有下界的通配符,限定A
和A
的父类。
即便容器的类型之间存在继承关系,但容器间是不构成继承关系的。所以需要将容器类型设置为<? extends T>
和<? super T>
来让容器间存在继承关系。
对于上界<? extends T>
类型,在编译期,只知道要存放T
和T
的子类,具体类型不知道,所以往容器内插入数据不被允许。但读数据时,可以隐式的转化为基类或者Object,所以读操作没有影响。
对于下界<? super T>
类型,规定了元素的最小粒度,必须是T
或其父类,所以往里添加T
或其子类都是可以的,因为可以隐式的转化为T类型。而读时无法转化为任意类型,只能用Object类存储。
注意:
- 上界
<? extends T>
不能往里存,只能往外取,适合频繁往外面读取内容的场景。 - 下界
<? super T>
不影响往里存,但往外取只能放在Object数组中,适合经常往里面插入数据的场景。
类型擦除
泛型信息只存在于编译阶段,在进入JVM前,所有的泛型信息都会被擦除,这就是类型擦除。
1 | public class Test { |
上面举例中编译后list1
和list2
的类型都为List.class
,泛型信息被擦除了。
在泛型类被类型擦除后,如果泛型类中没有指定上界,如<T>
,就会被转译为Object
类型;如果指定了上界,就会被转译为上界类型。
类型擦除保证了能与JDK5之前的代码兼容,但也带来了一些局限性,比如可以通过反射来绕过泛型。如:
1 | public class Test { |
可以看到利用反射可以向指定了类型为String
的集合中添加非String
类型的元素。