JAVA学习(七)| 泛型
泛型 - 廖雪峰的官方网站 (liaoxuefeng.com)
什么是泛型⁍
泛型就是定义一种模板,例如ArrayList<T>,然后在代码中为用到的类创建对应的ArrayList<类型>
向上转型⁍
类型ArrayList<T>可以向上转型为List<T>(T不能变成父类)
要特别注意:不能把
ArrayList<Integer>向上转型为ArrayList<Number>或List<Number>。
使用泛型⁍
1 | |
泛型接口⁍
在接口中使用泛型
Comparable<T>泛型接口:
1 | |
在使用的时候实现这个接口就可以比较对应的类型了
编写泛型⁍
泛型类型<T>不能用于静态方法
对于静态方法,我们可以单独改写为**“泛型”方法**,只需要使用另一个类型即可
1 | |
多个泛型类型⁍
泛型还可以定义多种类型。例如,我们希望Pair不总是存储两个类型一样的对象,就可以使用类型<T, K>
Java标准库的Map<K, V>就是使用两种泛型类型的例子。
擦拭法⁍
Java语言的泛型实现方式是擦拭法(Type Erasure)。
所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。
擦拭法决定了泛型<T>:
-
不能是基本类型,例如:
int;(只能是Integer)-
因为实际类型是
Object,Object类型无法持有基本类型1
Pair<int> p = new Pair<>(1, 2); // compile error!
-
-
不能获取带泛型类型的
Class,例如:Pair<String>.class;- 并不存在
Pair<String>.class,而是只有唯一的Pair.class
- 并不存在
-
不能判断带泛型类型的类型,例如:
x instanceof Pair<String>;- 同上
-
不能实例化
T类型,例如:new T()。- 擦拭后实际上变成了
new Object(); - 要实例化
T类型,我们必须借助额外的Class<T>参数: 擦拭法 - 廖雪峰的官方网站 (liaoxuefeng.com)
- 擦拭后实际上变成了
泛型方法要防止重复定义方法,例如:public boolean equals(T obj),编译器会阻止一个实际上会变成覆写的泛型方法定义。
子类可以获取父类的具体的泛型类型<T>。
1 | |
extends通配符⁍
使用Pair<? extends Number>使得方法接收所有泛型类型为Number或Number子类的Pair类型。
这种使用<? extends Number>的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型T的上界限定在Number了。
只能读不能写
- 方法内部可以调用获取
Number引用的方法,例如:Number n = obj.getFirst();; - 方法内部无法调用传入
Number引用的方法(null除外),例如:obj.setFirst(Number n);。
super通配符⁍
Pair<? super Integer>表示,方法参数接受所有泛型类型为Integer或Integer父类的Pair类型。
使用<? super Integer>通配符表示:只能写不能读
- 允许调用
set(? super Integer)方法传入Integer的引用; - 不允许调用
get()方法获得Integer的引用。
唯一例外是可以获取Object的引用:Object o = p.getFirst()。
换句话说,使用<? super Integer>通配符作为方法参数,表示方法内部代码对于参数只能写,不能读。
1 | |
⁍
PECS原则⁍
Producer Extends Consumer Super
如果需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它是消费者(Consumer),要使用super通配符。
无限定通配符⁍
1 | |
既不能读,也不能写,那只能做一些null判断
很少使用,可以用<T>替换,同时它是所有<T>类型的超类。
泛型和反射⁍
我们可以声明带泛型的数组,但不能用new操作符创建带泛型的数组:
1 | |
必须通过强制转型实现带泛型的数组:
1 | |
安全使用泛型数组⁍
使用泛型数组要特别小心,因为数组实际上在运行期没有泛型,编译器可以强制检查变量ps,因为它的类型是泛型数组。但是,编译器不会检查变量arr,因为它不是泛型数组。因为这两个变量实际上指向同一个数组,所以,操作arr可能导致从ps获取元素时报错,例如,以下代码演示了不安全地使用带泛型的数组:
1 | |
要安全地使用泛型数组,必须扔掉arr的引用:
1 | |
不能直接创建泛型数组T[],因为擦拭后代码变为Object[]:
必须借助Class<T>来创建泛型数组
1 | |
我们还可以利用可变参数创建泛型数组T[]:
1 | |
谨慎使用泛型可变参数⁍
后记
泛型好难懂
感觉简而言之不要用x




