泛型
2025/12/3大约 4 分钟
什么是泛型?
泛型是Java的重要特性,允许类、接口、方法在定义时使用类型参数,使用时再指定具体类型。
其核心目的是编译时类型检查,避免运行时类型转换异常,同时提升代码复用性。
为什么需要泛型?
- 代码复用:无需为不同数据类型编写重复代码。例如,未用泛型时,需重载
add(int a, int b)、add(float a, float b)等方法;用泛型后,可复用为一个方法:private static <T extends Number> double add(T a, T b) { return a.doubleValue() + b.doubleValue(); } - 类型安全:编译时约束集合或方法的类型,避免存入错误类型数据。例如,未用泛型时,
List可存入任意类型,取出时强制转换易出错;用泛型后,List<String>仅允许存入String,编译时即可拦截错误:// 未用泛型(风险) List list = new ArrayList(); list.add("string"); list.add(100); // 允许存入Integer,取出时转换易出错 // 用泛型(安全) List<String> list = new ArrayList<>(); list.add("string"); list.add(100); // 编译报错,不允许存入非String类型
泛型底层是如何实现的?
Java 的泛型是通过 类型擦除 实现的。
[?]类型擦除就是:泛型在编译后会被'擦掉',运行时不再保留具体泛型类型。
[?]List<String> list = new ArrayList<>();编译器知道: “这个 List 只能放 String”。
但 编译完之后的字节码里,类型信息会被擦掉,变成:
List list = new ArrayList(); // JVM 看到的是这个效果所以运行时 list.get(0) 返回的是 Object。
- 那为什么还能拿到 String?
因为 编译器在需要的地方给你自动补 cast(类型强制转换):
你的写法:
String s = list.get(0);字节码会变成:
String s = (String) list.get(0); // 编译器补的 cast为什么要这样?
因为 Java 为了兼容旧代码(Java5 前没有泛型),所以采用了:
泛型 = 编译期检查 + 运行期擦除
- 编译器会检查你是不是只放了 String
- 运行时 JVM 只看到一个普通的 List(没有类型)
- 编译器必须自动生成
(String)来确保类型安全