这一篇整理《Java核心技术 卷1》第 8 章的泛型。泛型把类型信息交给编译器检查,减少集合里放错类型和取出后强制转换。
一、泛型先解决类型安全
没有泛型时,集合里常放 Object,取值后再转换;写成 ArrayList<String> 后,编译器能检查添加和读取的类型:
java
ArrayList<String> names = new ArrayList<>();
names.add("Java");
String first = names.get(0);
二、泛型类和泛型方法
类型变量可以定义在类上,使用时再给出具体类型:
java
class Pair<T> {
private T first;
private T second;
}
泛型方法在返回类型前声明自己的类型变量。下面的方法假设列表非空:
java
public static <T> T first(List<T> values) {
return values.get(0);
}
三、类型限定
<T extends Comparable<T>> 表示 T 必须能够与同类型对象比较。即使限定的是接口,关键字仍然是 extends;多个限定用 & 连接,例如 <T extends Number & Comparable<T>>。如果有类限定,它只能有一个,并且要写在接口限定前面。
四、通配符和读写方向
Pair<Manager> 不是 Pair<Employee> 的子类型,泛型通常不会因为类的继承关系自动变成协变类型。
? extends Employee是上界:可以把读取结果当作Employee,但不能安全地放入某个具体的 Employee 值(null除外)。? super Manager是下界:可以放入Manager及其子类;读取时只保证得到Object。
常用口诀是 PECS:Producer Extends,Consumer Super。作为数据提供者时优先考虑 extends,作为数据接收者时优先考虑 super。
五、类型擦除
编译后的泛型参数通常会擦除为原始类型或它的上界,所以 ArrayList<String> 和 ArrayList<Integer> 运行时属于同一个泛型类。由于运行时没有完整保留具体类型参数,new T()、new T[10] 和 instanceof Pair<String> 都不允许;可以用 instanceof Pair<?> 检查是否为某种 Pair。
一句话回顾
泛型让编译器先检查类型;遇到容器边界时,按读取或写入方向选择 extends 与 super。