Java泛型采用类型擦除机制,编译时移除泛型参数,运行时仅保留Object或上界类型,导致无法在运行时获取泛型信息、不能使用instanceof判断具体参数化类型、不能new T()等。
T、List 全部干掉,运行时只剩 Object 或边界类型Java 的泛型不是“真泛型”,它只在编译期起作用。你写 List,编译器会检查你只往里加 String,但一编译完,字节码里就只剩 List——连 String 这个词都消失了。JVM 根本不知道你曾经声明过泛型。
)→ 擦除为 Object
)→ 擦除为 Number
? extends Comparable)→ 编译期校验后,运行时也擦成原始类型这也就是为什么下面两行代码的 getClass() 结果完全一样:
ArrayListlist1 = new ArrayList<>(); ArrayList list2 = new ArrayList<>(); System.out.println(list1.getClass() == list2.getClass()); // true
instanceof 判断 List?因为运行时根本不存在这个类型你写 if (obj instanceof List,编译器直接报错:泛型类型不可用于 instanceof。这不是语法限制,而是语义不可能——擦除后只有 List.class,没有 List。
ParameterizedType,且仅对字段、方法签名、父类声明等“静态位置”有效new ArrayList() )不携带泛型运行时信息Class 类型令牌(type token)new T()、不能 static T[] 、桥接方法悄悄生成这些不是“设计缺陷”,而是擦除机制的必然结果。你写的每行泛型代码,编译器都在背后补动作:
new T() 编译失败 → 因为擦除后 T 是 Object,但你没法在运行时知道该 new 哪个具体子类T → 静态属于类,而泛型参数属于实例化时的类型变量,二者生命周期不匹配Comparable 的 compareTo(Integer) 会被补一个 compareTo(Object),用来应付多态调用桥接方法看不见、摸不着,但用反射查 getDeclaredMethods() 就能看到它们的存在。
va 5 之前的全部生态Java 没有重写 JVM 来支持“真泛型”,是因为已有海量非泛型代码(比如 List、Map)和类库必须继续跑。如果泛型在运行时保留类型信息,那 List 和老 List 就是两个不兼容的类型,整个 JDK 集合框架就得推倒重来。
所以,擦除不是偷懒,是权衡:用编译期严格检查 + 运行时零开销 + 100% 二进制兼容,换掉了运行时类型可见性。你在 IDE 里享受的类型提示、安全的 get() 返回值,全是编译器在擦除前帮你拦下来的;而你在反射、序列化、动态代理里踩的坑,几乎都源于擦除后那一片空白。