泛型是编译期类型占位符,运行时被擦除;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免费学习笔记(深入)”;