泛型擦除与可具体化边界

泛型类型擦除如何工作:运行时可见什么、为何不能 new T[],以及桥接方法直觉。

#type / concept #status / growing #tech / dev #resource / java

[!info] 关联笔记

泛型擦除与可具体化边界

这个概念为什么出现

学了泛型仍会对 List<String> 运行时类型、异常捕获、数组创建感到困惑。根因是 Java 泛型主要是编译期机制,采用类型擦除兼容历史虚拟机。

[!abstract] 一句话理解 类型参数在编译后大多被擦除为上界(常为 Object),运行时集合主类型相同;因此不能依赖 T 做可具体化操作,数组与异常等有专门限制。

最小可运行示例

场景:运行时看两个参数化列表的类

import java.util.*;

public class ErasurePeek {
    public static void main(String[] args) {
        List<String> a = new ArrayList<>();
        List<Integer> b = new ArrayList<>();
        System.out.println(a.getClass() == b.getClass()); // true
        // if (a instanceof List<String>) {} // 编译不合法/无意义模式
        System.out.println(a instanceof List);
    }
}
javac ErasurePeek.java && java ErasurePeek

期望:

true
true

结合场景再看三个关注点

  1. 运行时类相同不代表编译期类型相同。
  2. 强制转换由编译器插入,不安全用法会在运行期炸。
  3. 需要保留类型时用超类型令牌/显式 Class 令牌等模式。

核心概念与准确模型

1. 擦除规则直觉

  • 无界 → Object
  • T extends Number → Number
  • 桥接方法保持多态

2. 不可做的事

  • new T() / new T[]
  • catch 可参数化异常类型
  • 运行时 instanceof List<String>

3. reification

Java 数组可具体化;泛型参数默认不可。

边界情况与反直觉行为

  1. 反射 getGeneric* 可看到签名信息(类文件属性),不等于完整运行时 reify。
  2. 原始类型触发未检查警告。
  3. 变参与泛型产生 heap pollution。

常见误区

[!warning] 常见误区:认为擦除=泛型没用 编译期安全与 API 表达仍然极有价值。

工程实践

  1. 避免 raw type。
  2. 需要运行时类型传 Class<T>/TypeToken
  3. 抑制警告仅限边界。
  4. 读 javap 看桥接方法建立直觉。

本节总结

  • 擦除解释运行时限制
  • 编译期仍强约束
  • 与数组 reify 对比记忆

自测题

  1. 为什么不能创建泛型数组 new T[n]
  2. 桥接方法为了什么?
参考答案
  1. 擦除与数组可具体化冲突,类型安全难保证。
  2. 在擦除后仍保持覆盖与调用的二进制兼容/多态分派。

延伸阅读与资料来源

资料类型支撑内容
Type Erasure Tutorial教程擦除
JLS 4.6 Type Erasure规范规则
创建于 2026/7/15 更新于 2026/7/15