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

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


















