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