Java泛型类型擦除在字节码层面有四条核心规律:1. 无界类型参数擦除为Object;2. 有界类型参数擦除为首个类上界;3. 泛型方法调用点自动插入checkcast类型转换;4. 泛型签名以Signature属性保留供反射读取。

Java 泛型类型擦除在字节码层面有明确、可预测的转换规律,是面试和 JVM 调优中高频考察点。核心不在于“泛型消失了”,而在于“它被怎么替换了、在哪插了转换、哪些信息其实还留着”。以下提炼出四条最常考、最实用的字节码转换结论,每条都对应真实编译行为和反编译验证依据。
无界类型参数一律擦除为 Object
源码中 <T>、<E> 这类未声明上界的泛型参数,在字节码里全部变成 Object:
- 字段声明:
private T data;→ 字节码中 descriptor 为Ljava/lang/Object; - 方法返回值:
public T get() { ... }→ 字节码中 signature 为()Ljava/lang/Object; - 方法参数:
public void set(T t)→ descriptor 为(Ljava/lang/Object;)V
有界类型参数擦除为首个上界(非泛型类)
只要写了 <T extends Number & Comparable>,擦除时只取第一个类边界 Number(接口边界会被丢弃):
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
private T num;→ descriptor 是Ljava/lang/Number;,不是Object -
public T getValue()→ 返回类型擦除为Number,调用处自动加(Number)强转 - 注意:第二个接口
Comparable不参与擦除,仅用于编译期约束
泛型方法调用点强制插入类型转换
擦除后的方法签名变“宽”,但编译器会在每次调用处补上安全转型——这是运行时不报 ClassCastException 的关键:
立即学习“Java免费学习笔记(深入)”;
List<String> list = new ArrayList<>(); list.add("a"); String s = list.get(0);- 字节码中
list.get(0)后紧跟checkcast java/lang/String - 该指令由 javac 自动生成,开发者不可见,但可通过
javap -c验证
原始类型名与泛型签名分离存储
虽然运行时 List<String> 和 List<Integer> 的 getClass() 相同,但泛型签名仍以字符串形式保留在 class 文件的 Signature 属性中:
- 字段、方法、类定义若含泛型,其
signature属性非 null(如"Ljava/util/List<Ljava/lang/String;>") - 该信息不进运行时堆内存,但可通过反射 API(如
Field.getGenericType())读取 - ASM、Byte Buddy 等字节码工具正是靠解析
Signature属性还原泛型结构

















