泛型集合通过编译期类型检查避免运行时ClassCastException:限定元素类型后,add非法类型被编译器拦截,get返回正确类型无需强转,消除转型失败风险。

泛型集合如何避免运行时 ClassCastException
没有泛型时,ArrayList 存什么都能收,取出来得手动强转:(String) list.get(0)。一旦误存了 Integer,运行到这行就抛 ClassCastException——错误被推迟到运行期,难定位。
泛型把类型检查提前到编译期:ArrayList 只允许加 String,编译器直接拦截 list.add(123) 这类操作。不是“防止出错”,而是让错在写完就暴露。
- 编译器生成桥接方法和类型擦除后的字节码,但校验逻辑全程参与
- IDE(如 IntelliJ)能实时高亮不匹配的 add / get 调用
- 注意:反射绕过泛型(如
list.getClass().getMethod("add", Object.class).invoke(list, 42))仍可破坏类型安全
get() 方法不再需要显式强制转换
以前写 String s = (String) list.get(i),括号不是装饰,是必须的生存操作;现在 ArrayList 的 get() 返回类型就是 String,直接赋值即可。
这不只是少敲几个字符——它消除了“转型失败”这个独立错误源,也降低了维护成本:改类型时,所有相关 get() 调用会随泛型参数自动适配,不用逐个翻找强转语句。
立即学习“Java免费学习笔记(深入)”;
- 泛型信息只存在于源码和编译期,运行时仍是
Object,但编译器已为你插入隐式转型(必要时) - 如果用原始类型(
new ArrayList())混入泛型集合,get()返回仍为Object,强转又得回来 - 使用
var list = new ArrayList时,() get()类型推导仍准确,但别对原始类型用var
为什么不能用 ArrayList 接收 ArrayList
这是常见误解:虽然 Integer 是 Number 的子类,但 ArrayList 并不是 ArrayList 的子类型——Java 泛型是**不变的(invariant)**,不是协变的。
否则你就能往里 add Double(合法于 Number),却破坏了原集合只应含 Integer 的契约。
- 需要读多写少场景?用通配符:
ArrayList extends Number>允许 get 出Number,但禁止 add(除null) - 需要写多读少?用
ArrayList super Integer>,可 addInteger,但 get 出来只是Object - 别试图靠类型转换绕过:
(ArrayList编译失败,加) list @SuppressWarnings("unchecked")是自欺欺人
泛型集合与原始类型混用时的警告和风险
当你把 ArrayList(无泛型)传给期望 ArrayList 的方法,编译器报 unchecked conversion 警告——这不是建议,是明确告诉你:“我无法保证类型安全,后果自负”。
这种混用常出现在老代码迁移、反射调用或某些框架 API(如早期 Spring 的 getBean(String, Class) 之前版本)中,隐患在于:集合内部可能藏了任意类型对象,而后续逻辑假设全是 String。
- 用
-Xlint:unchecked编译可捕获所有此类问题 - 禁用警告(
@SuppressWarnings("unchecked"))前,务必确认该处确实不会引入异质元素 - 第三方库返回原始集合时,优先用
new ArrayList(rawList)包一层再指定泛型,而非强转










