【Java SE 复盘】Java 集合框架之泛型详解

目录

一、引言:

二、什么是泛型?我能不用吗?

✅ 示例1:用了泛型

❌ 没用泛型(Java 5 之前)

三、泛型类 & 泛型方法:怎么写才对?

✅ 示例2:泛型类

使用:

✅ 示例3:泛型方法

调用:

四、通配符与 PECS 原则:别再被绕晕了!

🤔 我遇到的问题:

✅ 示例4:上界通配符 

✅ 示例5:下界通配符 

📌 PECS 原则(Producer Extends, Consumer Super)

五、类型擦除:泛型真的存在于运行时吗?

✅ 示例6:验证类型擦除

❌ 常见误区:为什么不能 new T()?

📌 类型擦除的影响总结:

六、泛型在集合中的作用:为什么集合都用泛型?

七、实战避坑指南:泛型常见误区

八、总结:泛型知识点速记表


一、引言:

作为一个即将毕业的 Java 新人,在刷面经的时候我发现,“泛型”几乎是每个大厂面试都会问到的知识点。刚开始我以为它只是个语法糖,写代码时方便而已。

但随着学习深入,我才意识到:泛型不仅是集合安全的保障,更是写出高质量代码的关键工具之一。

这篇文章就记录了我从“看不太懂”到“基本理解”的过程。如果你也觉得泛型难懂,或者在面试中被问得哑口无言,那这篇内容应该能帮你打通任督二脉!


二、什么是泛型?我能不用吗?

先说结论:你当然可以不用泛型,但代价是代码更容易出错,而且维护成本更高。

✅ 示例1:用了泛型

List<String> list = new ArrayList<>();
list.add("Hello");
String str = list.get(0); // 不需要强转

❌ 没用泛型(Java 5 之前)

List list = new ArrayList();
list.add("Hello");
list.add(123); // 看似没问题
String str = (String) list.get(1); // 运行时报错 ClassCastException

📌 面试常问:

“泛型的作用是什么?”

  • 提高类型安全性;
  • 减少强制类型转换;
  • 增强代码可读性。

三、泛型类 & 泛型方法:怎么写才对?

泛型最常见的两种应用方式是泛型类泛型方法

✅ 示例2:泛型类

public class Box<T> {
    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}
使用:
Box<String> stringBox = new Box<>();
stringBox.set("Java");
System.out.println(stringBox.get());

✅ 示例3:泛型方法

public class Util {
    public static <T> void printList(List<T> list) {
        for (T item : list) {
            System.out.println(item);
        }
    }
}
调用:
List<Integer> numbers = List.of(1, 2, 3);
Util.printList(numbers);

📌 小贴士:

  • 如果整个类都需要参数化类型,选泛型类
  • 如果只是某个方法需要用到泛型,用泛型方法即可;
  • 两者都可以结合使用。

四、通配符与 PECS 原则:别再被绕晕了!

这个部分可能是很多同学最头疼的地方:为什么有时候不能 add 元素?为什么返回值变成了 Object?

其实这都跟通配符有关。

🤔 我遇到的问题:

有一次我在遍历一个 List<? extends Animal>,想往里面加一只 Dog,结果编译器报错了。我当时一脸懵:不是说 Dog 是 Animal 的子类吗?为什么会不让加?

后来我才明白:虽然 Dog 是 Animal 的子类,但编译器不知道这个 List 到底装的是哪种 Animal 的子类。


✅ 示例4:上界通配符 <? extends T>

List<? extends Number> list = new ArrayList<Integer>();
// list.add(100); ❌ 编译错误
Number num = list.get(0); // ✅ 可以读取

📌 总结一句话:

extends 是只读的,只能读不能写。


✅ 示例5:下界通配符 <? super T>

List<? super Integer> list = new ArrayList<Number>();
list.add(100); // ✅ 可以添加 Integer 及其子类对象
Object obj = list.get(0); // ⚠️ 返回值是 Object,无法确定具体类型

📌 总结一句话:

super 是只写的,只能添加当前类型及子类,但读出来是 Object。


📌 PECS 原则(Producer Extends, Consumer Super)

这是《Effective Java》里提到的一个原则,用来判断什么时候该用哪种通配符:

场景用法
数据生产者(读取)? extends T
数据消费者(写入)? super T

📌 举个例子:

public static void copy(List<? super String> dest, List<? extends String> src) {
    for (String s : src) {
        dest.add(s);
    }
}

📌 面试高频题:

“请解释 <? extends T><? super T> 的区别?”

  • extends:只能读不能写;
  • super:只能写不能读;
  • PECS 原则是选择依据。

五、类型擦除:泛型真的存在于运行时吗?

这个问题是我最困惑的之一。我以为泛型是运行时存在的,结果发现它其实是个“假象”。

✅ 示例6:验证类型擦除

List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

System.out.println(list1.getClass() == list2.getClass()); // 输出 true

📌 说明:

  • 在运行时,这两个 List 的类型信息都被“擦掉了”,变成原始类型 ArrayList
  • 所以 JVM 根本不知道你放进去的是 String 还是 Integer。

❌ 常见误区:为什么不能 new T()?

public class Box<T> {
    T[] array = new T[10]; // ❌ 编译错误
}

📌 原因:

  • 因为泛型在运行时不存在,所以 JVM 不知道 T 是什么类型,也就没法创建数组或实例。

📌 解决办法:

  • 用反射传入 Class<T>
  • 或者通过传递数组实例。

📌 类型擦除的影响总结:

问题描述原因
不能 new T() 或 T[]类型擦除导致 JVM 不知道 T 是什么类型
不能获取泛型类型信息运行时泛型信息已被擦除
方法重载冲突类型擦除后两个方法签名相同,编译失败
桥方法编译器自动生成桥方法保持多态性

六、泛型在集合中的作用:为什么集合都用泛型?

Java 集合框架从一开始设计就考虑到了泛型的支持。比如:

  • List<E>
  • Set<E>
  • Map<K,V>

它们之所以广泛使用泛型,是因为:

  • 提升类型安全性:避免运行时 ClassCastException;
  • 增强可读性:一看就知道集合里放的是什么类型;
  • 简化开发:不需要频繁做类型转换。

七、实战避坑指南:泛型常见误区

问题描述正确做法
试图 new T() 或 new T[]用反射或传递 Class 对象解决
自定义类放入 HashSet 未重写 hashCode/equals导致去重失败
使用普通 for 循环删除集合元素抛出 ConcurrentModificationException
误用 <? extends T> 进行写入操作无法 add 元素
误用 <? super T> 进行读取操作返回值为 Object 类型

八、总结:泛型知识点速记表

核心知识点内容说明
泛型的作用提高类型安全性、减少强制类型转换
泛型形式泛型类、泛型接口、泛型方法
通配符? extends T(只读)、? super T(只写)
PECS 原则Producer Extends, Consumer Super
类型擦除编译时泛型信息被擦除,运行时不可见
桥方法保证泛型多态性的机制
泛型在集合中的作用提升集合的类型安全和代码可读性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值