Java泛型类型擦除体现为三方面:类型参数被替换为上界(如Object),调用处插入checkcast等强制转换,同时通过Signature属性保留泛型信息供反射使用;桥接方法则确保多态兼容。

要通过字节码分析 Java 泛型的类型擦除,关键不是“看泛型还在不在”,而是观察编译器做了三件事:把类型参数替换成上界(通常是 Object)、在调用处插入强制转换、保留泛型签名供反射使用。这些痕迹全藏在字节码里,用标准工具就能验证。
用 javap 查看擦除后的类结构
编译一个泛型类,比如:
public class Box<t> {
private T value;
public void set(T v) { this.value = v; }
public T get() { return value; }
}</t>
执行 javac Box.java 后,运行:
javap -c -s Box
你会看到:
- 字段 value 的签名是 Ljava/lang/Object;(不是泛型签名)
- 方法 set 和 get 的参数与返回类型也都是 Ljava/lang/Object;
- 但加上 -s 参数后,还能看到额外的 Signature 属性,例如 Signature: LBox; —— 这就是编译器为反射保留的原始泛型信息
关注强制类型转换的插入位置
写一段使用代码:
List<string> list = new ArrayList();
list.add("hello");
String s = list.get(0); // 这里没有显式强转,但字节码里有</string>
对包含这段逻辑的类执行 javap -c,在 get(0) 调用后,会立刻看到:
checkcast java/lang/String
这说明:编译器在你“看不见的地方”自动加了类型检查。擦除不是简单删掉泛型,而是用 运行时类型校验 来兜底。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
识别桥接方法(Bridge Method)
当泛型类被继承并特化时,比如:
class StringBox extends Box<string> { }</string>
用 javap -c -l StringBox 查看,会发现多出一个形如:
public java.lang.Object get();
这个方法体里只有一行 areturn,但它就是桥接方法。它的存在是为了让 JVM 多态调用不崩溃——因为父类 Box.get() 擦除后返回 Object,而子类重写的方法签名是 String get(),二者字节码签名不同,必须靠桥接方法来衔接。
对比 Signature 属性和实际 descriptor
字节码中每个成员都有两个关键描述符:
- descriptor:真实运行时类型(擦除后),如 (Ljava/lang/Object;)V
-
Signature:编译期泛型签名(保留在常量池),如 (TT;)V 或
反射 API(如 Method.getGenericReturnType())读的就是 Signature,而不是 descriptor。这也是为什么运行时还能“部分获取”泛型类型——不是没擦除,而是擦除得有选择性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










