java泛型仅存在于编译期,运行时被类型擦除为原始类型;反射无法“恢复”泛型,仅能通过显式声明(如方法返回类型)获取部分泛型信息,实际类型安全需依赖class对象或显式传入类型凭证。

Java泛型在运行时根本不存在,反射能绕过检查,不是因为它“破解”了什么,而是它压根就看不到泛型——因为泛型早已被编译器擦除,字节码里只剩原始类型。
泛型只活在编译期,运行时只剩“裸类型”
像 List<string></string> 和 List<integer></integer>,编译后都变成单纯的 List。JVM 加载的 class 文件中没有 <string></string> 这类标记,所有泛型参数都被替换为 Object(或其上界)。这意味着:
- 调用
list.getClass()得到的是ArrayList.class,不是ArrayList<string>.class</string> -
list.getClass().getMethod("add", Object.class)能成功获取方法,因为字节码里add的签名确实是add(Object) -
instanceof List<string></string>编译直接报错——JVM 不支持带泛型的类型判断
反射调用 add() 成功,但隐患埋在取值时
往 List<string></string> 中用反射塞进一个 Integer,不会抛异常;add 操作只校验参数是否为 Object 子类,而 Integer 当然满足。问题出在后续使用环节:
-
String s = list.get(0):编译通过,运行时强转失败,抛ClassCastException -
for (String s : list):第一次迭代遇到非 String 元素就崩 -
list.stream().map(String::length):map 阶段对 Integer 调用length(),立刻报错
getGenericXxx() 并不等于“恢复泛型”,只是看声明位置
像 getGenericReturnType() 或 getGenericType() 看起来能返回 List<user></user>,但这仅适用于**源码中显式写出泛型的地方**,比如方法返回类型、父类继承声明:
- ✅
public List<user> getUsers() { ... }</user>→ 可通过反射拿到List<user></user> - ❌
List<user> list = new ArrayList();</user>→list.getClass().getGenericSuperclass()返回ArrayList,无泛型信息 - ❌
list.getClass().getMethod("get", int.class).getGenericReturnType()返回Object,因为ArrayList.get()原本就声明返回Object
真正安全的做法:靠 Class 对象,而不是靠泛型签名
框架如 Jackson、MyBatis 能可靠处理泛型,并非依赖反射“读到了类型”,而是靠开发者显式传入运行时类型凭证:
- 用
new TypeReference<list>>() {}</list>匿名子类,利用其超类签名保留泛型信息 - 手动传
Class<user></user>参数,让类型信息以对象形式存活于堆中 - 避免仅凭
getGenericParameterTypes()结果做 newInstance() 或反序列化决策










