Java泛型运行时虽被擦除,但声明位置(父类继承、字段、方法签名)的泛型信息保留在字节码中,可通过反射获取;TypeToken可捕获任意泛型类型;仅局部变量等场景无法获取。

Java 泛型在运行时被擦除,但只要泛型信息出现在**声明位置**(如父类继承、字段类型、方法签名),就能通过反射拿到真实类型参数。关键不是看对象实例,而是看字节码里有没有保留泛型签名。
从父类继承关系中提取
这是最稳定的方式。子类必须显式继承带具体实参的泛型父类,例如 class UserService extends BaseServiceBaseService<user></user> 的完整签名。
- 调用
getClass().getGenericSuperclass(),检查返回值是否为ParameterizedType - 如果是,用
getActualTypeArguments()[0]获取第一个泛型实参 - 若该实参是
Class>类型(如User.class),可直接强转为Class<t></t> - 若为
ParameterizedType(如List<string></string>),需递归解析其getActualTypeArguments() - 跳过
TypeVariable(如T)和WildcardType(如? extends Number),它们没有运行时具体类型
从字段或方法签名中获取
成员变量或方法若声明了泛型类型,其泛型信息也保留在字节码中。比如 private List<order> orders;</order> 或 public Map<string list>> getData()</string>。
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- 对字段:用
field.getGenericType()(不是field.getType()) - 对方法:用
method.getGenericReturnType()或method.getGenericParameterTypes() - 判断返回的
Type是否为ParameterizedType - 调用
getRawType()得到原始类型(如List.class),再用getActualTypeArguments()拿实参 - 嵌套泛型要逐层展开:先取
Map<k></k>的V,再判断它是否仍是ParameterizedType
用 TypeToken 捕获任意泛型类型
当无法依赖继承结构(比如泛型类直接实例化),又需要支持复杂类型(如 List<string></string>、Map<string extends number></string>),推荐使用 Guava 的 TypeToken。
- 写法:
new TypeToken<list>>() {}</list>—— 注意末尾的{},这是创建匿名子类的关键 - 它利用子类的
getGenericSuperclass()提取泛型信息,比手动反射更健壮 -
typeToken.getType()返回完整的Type,typeToken.getRawType()可得Class主体 - 适用于序列化、类型安全容器、泛型工厂等场景
哪些情况真的拿不到?
类型擦除不可逆,以下场景反射也无能为力:
- 仅声明泛型变量但未在结构中固化:如
List<string> list = new ArrayList();</string>→ 运行时只剩ArrayList.class - 泛型出现在局部变量、方法形参(无注解)、或泛型方法体内
- 子类继承的是原始类型(
extends BaseService)或类型变量(extends BaseService<t></t>) - 静态方法中含未绑定的类型变量(如
<t> T get()</t>),反射只返回TypeVariable
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










