泛型是编译期类型占位符,运行时被擦除;ArrayList不能赋值给ArrayList,因泛型具不变性,否则破坏类型安全。

泛型本质是编译期的类型占位符
Java 泛型不是运行时特性,而是编译器用来做类型约束的语法糖。它不改变字节码结构,也不会生成多个类版本(不像 C++ 模板)。你写 List,底层实际还是 List,只是编译器在编译阶段插入类型检查,并在必要时自动插入强制转换。
这意味着:
- 运行时通过 obj.getClass() 拿不到泛型信息(类型擦除)
- 不能用 new T() 或 T.class —— 因为 T 在运行时已不存在
- instanceof 不能用于参数化类型,比如 if (x instanceof List 是编译错误,只能写 if (x instanceof List)
为什么 ArrayList 不能赋值给 ArrayList
这是泛型「不变性」(invariance)的直接体现。尽管 String 是 Object 的子类,但 ArrayList 和 ArrayList 在类型系统中互不兼容——否则会破坏类型安全。
举个反例就明白:
如果允许 ArrayList,那就能执行 objs.add(new Date()),结果 strs 里混入了非 String 对象,后续遍历时 String s = strs.get(0) 就会抛 ClassCastException。
要实现安全的向上转型,得用通配符:
- ArrayList extends Object> 可接收 ArrayList(只读)
- ArrayList super String> 可接收 ArrayList(可写入 String)
Class 与泛型类的类型获取限制
泛型类如 class Box 在运行时无法获得 T 的具体类型,因为擦除后只剩原始类型 Box。但有例外:当泛型类型信息被保留在类签名或父类继承链中时,可通过反射提取。
常见可行场景:
- 子类继承泛型父类并固化类型: class StringBox extends Box → 用 StringBox.class.getGenericSuperclass() 能拿到 Box
- 方法返回类型带泛型: public List → 用 Method.getGenericReturnType() 可解析出 List
- 构造函数/字段声明含泛型:字段 private Map → Field.getGenericType() 返回 Map
注意:这些只是「声明时的类型信息」,不代表运行时对象的真实内容类型,也不能靠它绕过擦除做实例化。
立即学习“Java免费学习笔记(深入)”;
泛型方法中的类型推断常被误用
泛型方法如 的类型推断依赖实参,但推断规则有时反直觉。比如:
String s = pick("a", new Object()); // 编译失败:无法统一 T 为 String 和 Object更隐蔽的问题出现在泛型方法 + 通配符组合时:
List extends Number> nums = Arrays.asList(1, 2.0); Number n = max(nums); // 假设 maxT max(Collection ),这里会报错:? extends Number 不是具体类型
解决方式通常是显式指定类型参数:Number n = Utils.,或改用更宽松的签名:。
真正容易被忽略的是:泛型方法的类型变量作用域仅限该方法,和类声明的 无关;二者同名也不代表同一类型。










