泛型集合通过编译期类型检查解决运行时ClassCastException问题,禁止非法添加、免去手动强转,但擦除后仍为Object数组,且无法约束反射或数组协变等场景。

泛型集合解决了运行时 ClassCastException 问题
没用泛型的 ArrayList 存什么都能加,取出来却要手动强转——一旦类型不匹配,程序直到运行时才崩溃。比如往原始 ArrayList 里混入 String 和 Integer,取 Integer 时遇到 String 就抛 ClassCastException。
- 泛型在编译期做类型检查,
ArrayList不允许add(123)这种操作,直接报错 - 取元素时无需显式强转:
String s = list.get(0),而不是String s = (String) list.get(0) - 擦除后字节码仍是
Object数组,但编译器自动插入类型检查和强制转换,把错误提前到开发阶段
ArrayList 和原始 ArrayList 的实际行为差异
表面看都是存对象,但泛型版本在编译后生成的桥接方法、类型检查逻辑完全不同。原始类型集合本质上是“类型开放”的容器,而泛型集合是“契约明确”的容器。
- 原始
ArrayList可以被任何泛型变量引用(如ArrayList),但会触发编译警告list = new ArrayList() unchecked conversion - 向
ArrayList添加null合法,但添加new Object()编译失败 - 反射绕过泛型检查仍可能成功(如用
list.getClass().getMethod("add", Object.class).invoke(list, "hello")),但这是故意破坏契约,不属于正常使用场景
泛型不能解决数组协变引发的运行时异常
Java 数组是协变的(String[] 是 Object[] 的子类型),但泛型集合不是。这导致一个经典陷阱:试图把泛型集合转成数组时容易踩坑。
ArrayListlist = new ArrayList<>(); list.add("a"); // ❌ 错误写法:运行时抛出 ArrayStoreException String[] arr1 = (String[]) list.toArray(); // toArray() 返回 Object[] // ✅ 正确写法:传入带类型的数组 String[] arr2 = list.toArray(new String[0]); // 或更安全:list.toArray(new String[list.size()])
-
toArray()无参重载永远返回Object[],强转成具体类型数组是危险的 - 泛型本身无法阻止你用反射或原始类型绕过检查,它只保障「常规编码路径」下的类型安全
- 泛型不适用于基本类型,必须用包装类(
Integer而非int),这是性能与安全之间的权衡
泛型集合对 API 设计和维护的实际影响
一个返回 ArrayList 的方法,调用方永远得猜里面装的是什么;而返回 ArrayList,IDE 自动补全、编译检查、文档语义都立刻清晰。
立即学习“Java免费学习笔记(深入)”;
- 方法签名自带契约:
public表明输入输出类型一致List filter(List src, Predicate p) - 泛型方法可复用,但注意类型擦除后无法在运行时判断
T是String还是Integer(比如不能写if (T == String.class)) - 通配符(
? extends Number、? super Integer)用于放宽边界,但过度使用会让调用方困惑,应优先用具体类型参数








