泛型不是语法糖,而是编译期类型检查机制;其核心价值在于编译阶段拦截不安全操作,避免运行时ClassCastException,并通过不变性、类型参数约定及禁止泛型数组等设计保障类型安全。

泛型不是语法糖,是编译期类型检查机制
Java泛型在字节码层面会被擦除(type erasure),但它的核心价值不在于运行时保留类型信息,而是在编译阶段拦截不安全的类型操作。比如 List 声明后,编译器会拒绝 list.add(123) 这类调用——哪怕底层 ArrayList 实际存储的是 Object[]。
常见误解是“泛型只为避免强转”,其实更关键的是:它把原本运行时才暴露的 ClassCastException 提前到编译期报错,比如:
Listnums = new ArrayList<>(); nums.add("hello"); // 编译失败:incompatible types
这比等到 Integer i = (Integer) nums.get(0); 才抛异常要可靠得多。
为什么不能用 List 接收 List?
这是泛型协变(covariance)限制导致的,Java 中泛型是**不变的(invariant)**。即使 String 是 Object 的子类,List 和 List 之间也不存在继承关系。
立即学习“Java免费学习笔记(深入)”;
-
List—— 编译报错list = new ArrayList - 反例:如果允许,就能通过
list.add(new Object())往本该只存String的容器里塞任意对象,破坏类型安全 - 若需协变语义,得用通配符:
List extends Object>可接收List、List等 - 但注意:
? extends T容器只能读、不能写(除了null)
T、?、E 这些符号到底怎么选?
它们不是随意命名,而是约定俗成的类型参数标识,影响可读性和工具支持:
-
E(Element):用于集合类,如ArrayList、LinkedList -
T(Type):通用类型参数,最常用,如publicT getFirst(List list) -
K/V(Key/Value):专用于映射结构,如HashMap -
?(通配符):只用于变量声明或形参,不能用于定义类或方法的类型参数;? extends Number表示上界,? super Integer表示下界 - 错误用法:
class Box>或void method(? x)—— 编译不通过
泛型数组创建为何总报 Generic array creation 错误?
因为类型擦除后,JVM 无法在运行时确认数组元素的具体类型,而数组需要在创建时知道组件类型(如 new String[10])。所以 new ArrayList 或 new T[10] 都非法。
常见绕过方式:
- 改用
ArrayList等集合:它内部用Object[]+ 强转模拟,由泛型约束保障安全 - 用反射创建(不推荐):
(T[]) new Object[10]—— 会触发 unchecked 警告,且失去数组的类型检查能力 - 传入
Class实参,再调用Array.newInstance(clazz, size) - 最稳妥做法:避免泛型数组,优先使用参数化集合
真正容易被忽略的是:即使你用 @SuppressWarnings("unchecked") 压制警告,也无法让 JVM 在运行时校验数组元素类型——这个风险必须由开发者自己承担。








