CSNotesCSNotes
MySQL
Java
SSM
Redis
学习路线
TODO
实用工具
GitHub
MySQL
Java
SSM
Redis
学习路线
TODO
实用工具
GitHub
  • 第一章 Java 基础

    • 一、OOP 三大特性
    • 二、8 种基本数据类型
    • 三、BigDecimal
    • 四、类的访问权限
    • 五、包装类
    • 六、继承
    • 七、多态
    • 八、final 关键字
    • 九、static 关键字
    • 十、抽象类
    • 十一、接口
    • 十二、四种内部类
    • 十三、Lambda+ 函数式接口
    • 十四、枚举 enum
    • 十五、反射
    • 十六、注解
    • 十七、泛型
  • 第二章 集合

    • 一、集合框架总览
    • 二、ArrayList 源码解析
    • 三、LinkedList 源码分析
    • 四、HashSet
    • 五、String 底层结构
    • 六、String 常量池与 equals
    • 七、StringBuffer
    • 八、时间类:4 代演进史
    • 九、Stream 流
    • 十、工具类设计规范
    • 十一、Assert
    • 十二、常用工具类
  • 第三章 设计模式

    • 一、七大设计原则
    • 二、单例模式(饿汉式)
    • 三、装饰器模式
    • 四、策略模式
  • 第四章 异常

    • 一、Java 异常体系
    • 二、异常(完整版)
  • 第五章 并发

    • 一、多线程基础 JUC
    • 二、多线程进阶
    • 三、线程池
  • 第六章 JVM

    • 一、JSON 库
  • 第七章 RPC

    • 一、RPC 定义
  • 第八章 消息队列

    • 一、MQ 业务解耦
  • 第九章 JDK17 新特性

    • JDK17 新特性
    • 项目

十七、泛型

泛型=类型标签 + 编译器检查

我要把员工按部门分组,没有泛型只能写:

List list = new ArrayList();
list.add(new Employee());
Employee e = (Employee) list.get(0);

每次都强转,还可能转型错;有了泛型之后:

List<Employee> list = new ArrayList<>();
list.add(new Employee());
Employee e = list.get(0);

这样更安全,也不需要重复手动强制转换。

1. 泛型是什么

泛型就是在类、接口、方法中声明类型参数,用来约束容器里存放的数据类型。

例如:

List<String> names = new ArrayList<>();
List<Integer> ages = new ArrayList<>();

这里的 String、Integer 是类型参数,编译器会在编译阶段检查是否传入了正确的类型。

2. 编译期类型检查

泛型最大的好处,是在编译期就发现错误,而不是等到运行时才崩掉。

List<String> names = new ArrayList<>();
names.add("Alice");
// names.add(123); // 编译错误

如果没有泛型,程序可能在运行时出现 ClassCastException:

List list = new ArrayList();
list.add("Alice");
Integer i = (Integer) list.get(0); // 运行时抛异常

泛型让错误提前暴露,减少了很多隐藏 bug。

3. 运行时类型擦除

Java 的泛型不是在运行时保留真实类型参数的。它在字节码中会被“擦除”,也就是编译器在生成字节码时,去掉泛型信息。

例如:

List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();

这两个变量在运行时实际都对应同一个原始类型:

List.class

也就是说:

List<String> == List<Integer> == List

在字节码层面,都是 java.util.List。

所以,List<String> 和 List<Integer> 在运行时是同一个 Class,原因是:

  • 泛型只存在于源代码和编译期
  • 编译器负责检查类型安全
  • 运行时真正存在的是擦除后的原始类型

4. 为什么是“同一个 Class”

因为 JVM 不保存泛型参数信息,JVM 看到的只是:

class ArrayList implements List

而不是:

class ArrayList<String>

这也解释了为什么反射里拿到的泛型信息有限,很多时候只能看到 List.class,而看不到 String 或 Integer。

5. 但为什么还需要泛型

虽然运行时没保留泛型参数,但泛型仍然非常有用,因为它:

  • 提供编译期检查
  • 避免大量强制类型转换
  • 提高代码可读性
  • 降低运行时错误概率

例如:

List<String> names = new ArrayList<>();
String first = names.get(0);

这里 get(0) 直接返回 String,无需再写:

String first = (String) names.get(0);

6. 一个直观的理解

可以把泛型理解为:

  • 编译期:给容器“贴上标签”
  • 运行时:标签会被擦掉,只留原始类型

也就是说,泛型更像是“编译器的约束机制”,而不是 JVM 运行时的真实类型系统。

7. 总结

泛型的核心是:

  • 在编译期检查类型
  • 在运行时类型擦除
  • 让 List<String> 和 List<Integer> 在 JVM 中都变成同一个原始类型 List

所以,答案是:

List<String> 和 List<Integer> 是同一个 Class,原因是泛型是在编译器处理的,而不是 JVM 运行时保留的。

这是 Java 泛型设计中的关键特性,也正是它既安全又简洁的原因之一。

编辑此页
上次更新: 2026/9/26 19:37
Prev
十六、注解