讲解
前面写 List 时那对尖括号就是泛型(generics):它让类和方法在定义时不指定具体类型,用类型参数 T 占位,使用时再填上真实类型。没有泛型的年代,集合只能存 Object,取出来要强制转换,转错类型到运行时才炸;有了泛型,List 里塞进一个 Integer 在编译期就被拦下——泛型的本质是「把类型错误从运行时提前到编译期」,同时免掉了满屏的强转。
定义自己的泛型类:class Box { private T content; ... },使用时 Box b = new Box<>()(右侧菱形 <> 让编译器推断,不用重复写)。类型参数的名字是约定俗成的:T(Type)、E(Element)、K/V(Key/Value)。泛型方法把类型参数写在返回值前面:public static T first(T[] arr)。需要限制类型范围时用有界类型参数 <T extends Comparable>——要求 T 必须是可比较的,方法体里才能安全调 compareTo。
两个容易踩的高级特性。一是类型擦除:泛型只在编译期存在,编译后 List 和 List 都是同一个 List.class,所以 new T()、T[] 数组创建、instanceof List 都行不通。二是泛型不支持基本类型:没有 List,必须用包装类 List,装拆箱由编译器自动完成。泛型通配符 ? extends / ? super(PECS 原则)属于进阶内容,入门阶段先把类和方法的泛型用熟。
示例
自定义泛型类 Box:一份代码装任意类型:
class Box<T> {
private T content;
public void put(T item) {
this.content = item;
}
public T get() {
return content;
}
public boolean isEmpty() {
return content == null;
}
}
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.put("一封信");
System.out.println("字符串盒子:" + stringBox.get());
Box<Integer> intBox = new Box<>();
intBox.put(42);
System.out.println("整数盒子:" + (intBox.get() * 2));
System.out.println("空吗?" + intBox.isEmpty());
}
}
泛型方法与有界类型参数:写一个能比较任何 Comparable 类型的 max:
public class Main {
public static <T> void printAll(T[] arr) {
for (T item : arr) {
System.out.println("元素:" + item);
}
}
public static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
public static void main(String[] args) {
printAll(new String[]{"红", "绿", "蓝"});
System.out.println("较大值:" + max(3, 7));
System.out.println("较大值:" + max("apple", "banana"));
System.out.println("较大值:" + max(2.5, 1.8));
}
}
常见坑
- 想用泛型装基本类型:List 编译错误,写 List。注意大量装拆箱有性能开销,密集数值计算仍可用数组。
- 在泛型类里 new T() 或 new T[]:类型擦除后编译器不知道 T 是什么,这两样都被禁止。需要创建实例时把 Class 或工厂传进来(进阶手段),入门期知道「不行」即可。
- 裸类型(raw type)警告当耳旁风:写 List list = new ArrayList() 不带参数等于放弃类型检查,塞错类型运行时 ClassCastException。永远给集合加上类型参数。
- 以为 List 是 List 的子类型:泛型不随元素类型的继承而继承,List 不能赋给 List 变量——这正是通配符 ? extends 存在的理由。
小结
泛型把类型错误提前到编译期并免除强转;泛型类、泛型方法、有界类型参数三件套;记住类型擦除与包装类两条限制。下一节学习让代码大幅瘦身的 Lambda 与 Stream。