泛型是编译期类型占位符,运行时被擦除,导致无法获取具体类型信息、不能创建泛型数组、基本类型需用包装类,但能提前校验类型安全并提升代码复用性。

泛型是编译期的类型占位符,不是运行时的类型
Java 泛型本质是「类型参数化」——用 T、E、K 这类标识符代替具体类型,在编译阶段插入类型检查,但擦除后生成的字节码里没有泛型信息。这意味着:ArrayList<string></string> 和 ArrayList<integer></integer> 在 JVM 中都是 ArrayList,运行时无法获取泛型实参。
所以你不能写 if (list instanceof ArrayList<string>)</string>,会编译报错;也不能通过反射直接拿到 String 类型——这是类型擦除带来的根本限制。
避免强制类型转换和 ClassCastException
没用泛型时,从 ArrayList 取元素要手动强转:(String) list.get(0),一旦存入非 String 对象,运行时才崩,错误延迟暴露。
用了泛型后,编译器在赋值、取值、遍历时就做校验:
立即学习“Java免费学习笔记(深入)”;
-
ArrayList<string> list = new ArrayList();</string>→ 只允许 addString -
String s = list.get(0);→ 不需要 cast,也不报 warning -
list.add(123);→ 编译直接报错:add(String)无法接受int
这种提前拦截比靠测试或运气发现类型问题可靠得多。
泛型让集合类真正可复用,而不是靠 Object 拼凑
早期 Java(1.4 前)只能用 ArrayList 存任意对象,但业务逻辑往往绑定具体类型:比如一个工具方法专门处理「用户列表」或「订单 ID 列表」。不用泛型就得写 N 个重载或反复 cast,既啰嗦又易错。
泛型把「行为一致、仅类型不同」的逻辑抽象出来:
public static <T> T findFirst(List<T> list, Predicate<T> p) {
return list.stream().filter(p).findFirst().orElse(null);
}
这个方法能同时用于 List<user></user>、List<long></long>、List<localdatetime></localdatetime>——类型由调用方决定,逻辑只写一遍。
注意:<t></t> 必须声明在方法返回值前,否则编译不认;而且 T 不能用于静态字段或泛型类的静态方法参数,因为静态上下文不依赖实例类型。
泛型不是万能的:数组、基本类型、反射场景要格外小心
泛型和数组天生不兼容:new ArrayList<string>[10]</string> 编译失败,因为泛型类型不可实例化。得写成 new ArrayList[10](丢失类型),或改用 List<list>></list>。
基本类型不能直接作泛型实参:ArrayList<int></int> 非法,必须用包装类 Integer,带来自动装箱开销;OptionalInt 这类特化类就是为绕过这问题设计的。
反射几乎拿不到泛型实际类型:比如方法返回 List<user></user>,用 method.getGenericReturnType() 能拿到带泛型的 Type,但若类型被擦除(如字段声明为 List list),就只剩裸 List。
这些边界情况不是设计缺陷,而是泛型为兼容旧代码做的取舍——理解擦除机制,才能避开最常踩的坑。

















