E和T是泛型中约定俗成的类型参数标识符,非关键字;E代表Element,常用于集合类如List<E>,T代表Type,更通用,适用于任意泛型场景。

泛型中的 E 和 T 是类型参数的命名约定,不是关键字,本质都是占位符;泛型擦除是 Java 为兼容旧版本字节码而采用的编译期机制,并非设计缺陷,而是权衡类型安全与 JVM 兼容性的结果。
E 和 T 的含义与使用场景
E(Element)常用于集合类,强调“元素”语义,比如 List<E>、ArrayList<E>,表示该集合存放的是某种类型的元素;T(Type)更通用,泛指任意具体类型,常见于工具类、容器包装类或泛型方法中,如 public <T> T getFirst() 或 Box<T>。
二者没有语法区别,替换为 A、U 甚至 MyType 都能编译通过,但按惯例使用能提升可读性与协作效率。
- E 多见于 JDK 集合框架源码(如
AbstractCollection<E>) - T 更适合自定义泛型类或方法,尤其是不特指“集合元素”的场景(如
Result<T>、Mapper<T, R>) - 实际开发中,一个类里通常只用一种主类型参数,避免混用 E/T 造成理解混乱
泛型擦除的底层原因
Java 在 1.5 引入泛型时,JVM 字节码规范已稳定多年,无法直接支持“运行时保留完整泛型信息”。为避免修改 JVM、破坏已有 class 文件兼容性,编译器选择在编译阶段将泛型类型擦除:所有 <String>、<Integer>、<T> 全部替换成其边界类型(无显式上界则为 Object),并插入必要的强制类型转换代码。
- 擦除后,
ArrayList<String>和ArrayList<Integer>在运行时都是同一个原始类型ArrayList - 方法签名中泛型参数不参与重载判断(如
void f(List<String>)和void f(List<Integer>)不能共存) - 反射可绕过编译检查添加非法类型(
list.add("abc")到List<Integer>),因为运行时已无类型约束
擦除带来的关键影响与应对思路
擦除不是“去掉类型”,而是把类型检查前移到编译期,用牺牲部分运行时能力换取向后兼容和轻量级实现。
- 无法在运行时获取泛型实际类型(
list.getClass().getTypeParameters()返回空数组) - 静态上下文中不能使用类型参数(
public static T instance;编译报错,因静态变量属于类而非实例,而擦除后类只有一个) - 不能创建泛型数组(
new T[10]不合法,因运行时不知道 T 是什么) - 通配符
?和边界<? extends Number>是擦除下保持一定灵活性的补充手段
本质上,Java 泛型是“编译期契约 + 运行时裸类型”的组合。理解 E/T 的语义约定有助于写出清晰 API,而看清擦除机制的动因与限制,才能避开陷阱、合理使用反射或 TypeReference 等变通方案。

















