Appearance
泛型
泛型简介
为什么要使用泛型?
┌─────────────────────────────────────────────────────────────────┐
│ 泛型解决的问题 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ❌ 没有泛型时 │
│ ───────────────────── │
│ List list = new ArrayList(); │
│ list.add("hello"); │
│ list.add(123); │
│ String str = (String) list.get(1); // ClassCastException! │
│ │
│ ✅ 有泛型后 │
│ ───────────────────── │
│ List<String> list = new ArrayList<String>(); │
│ list.add("hello"); │
│ // list.add(123); // 编译错误! │
│ String str = list.get(0); // 无需强转,安全! │
│ │
└─────────────────────────────────────────────────────────────────┘泛型的好处
- 类型安全 - 编译时检查,减少运行时 ClassCastException
- 消除强制类型转换 - 代码更简洁
- 提高代码可读性 - 一目了然知道集合里放什么类型
泛型类
基本定义
java
// 泛型类 - T 是类型参数,可以是任意标识符
public class Box<T> {
private T value;
public Box() { }
public Box(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
// 使用
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
String value = stringBox.getValue(); // 无需强转
Box<Integer> intBox = new Box<>(123);
Integer num = intBox.getValue();多参数泛型
java
// 多个类型参数
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
// 使用
Pair<String, Integer> pair = new Pair<>("age", 25);
Pair<String, List<Integer>> complexPair = new Pair<>("numbers",
new ArrayList<>(Arrays.asList(1, 2, 3)));泛型接口
java
// 泛型接口
public interface Container<T> {
void add(T item);
T get(int index);
int size();
}
// 实现方式1:指定具体类型
public class StringContainer implements Container<String> {
private List<String> list = new ArrayList<>();
@Override
public void add(String item) {
list.add(item);
}
@Override
public String get(int index) {
return list.get(index);
}
@Override
public int size() {
return list.size();
}
}
// 实现方式2:保留泛型
public class GenericContainer<T> implements Container<T> {
private List<T> list = new ArrayList<>();
@Override
public void add(T item) {
list.add(item);
}
@Override
public T get(int index) {
return list.get(index);
}
@Override
public int size() {
return list.size();
}
}泛型方法
基本语法
java
public class GenericMethodDemo {
// 泛型方法 - 修饰符后返回值前声明 <T>
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
// 泛型方法可以有多个类型参数
public static <K, V> void printPair(Pair<K, V> pair) {
System.out.println(pair.getKey() + ": " + pair.getValue());
}
// 返回类型也可以是泛型
public static <T> T getFirst(T[] array) {
if (array.length == 0) {
return null;
}
return array[0];
}
}
// 调用
String[] strs = {"a", "b", "c"};
Integer[] nums = {1, 2, 3};
GenericMethodDemo.printArray(strs); // 推导类型
GenericMethodDemo.printArray(nums);类型限定(Bounded Type)
上限限定
java
// T 必须是 Number 或其子类
public class NumberBox<T extends Number> {
private T value;
public void setValue(T value) {
this.value = value;
}
public double toDouble() {
return value.doubleValue(); // 可以调用 Number 的方法
}
}
// 使用
NumberBox<Integer> intBox = new NumberBox<>();
intBox.setValue(10);
double num = intBox.toDouble();
NumberBox<Double> doubleBox = new NumberBox<>();
doubleBox.setValue(3.14);
// NumberBox<String> strBox; // 编译错误,String 不是 Number 子类多重限定
java
// T 必须同时实现 Comparable 和 Serializable
public class ComparableBox<T extends Comparable<T> & Serializable> {
private T value;
public T getMax(T other) {
return value.compareTo(other) > 0 ? value : other;
}
}泛型通配符
三种通配符
| 通配符 | 名称 | 特点 | 使用场景 |
|---|---|---|---|
<?> | 无限定通配符 | 可以接收任意类型 | 只读不写 |
<? extends T> | 上限通配符 | 可以接收 T 及其子类 | 读取数据(Producer) |
<? super T> | 下限通配符 | 可以接收 T 及其父类 | 写入数据(Consumer) |
示例详解
java
// ? extends - 生产者(读取)
public static double sumOfList(List<? extends Number> list) {
double sum = 0;
for (Number n : list) { // 可以读取为 Number
sum += n.doubleValue();
}
// list.add(1); // 编译错误!不能写入
return sum;
}
// ? super - 消费者(写入)
public static void addNumbers(List<? super Integer> list) {
list.add(1); // 可以写入 Integer
list.add(2);
// Integer num = list.get(0); // 读取类型是 Object
}
// 无限定通配符
public static void printList(List<?> list) {
for (Object obj : list) { // 只能读取为 Object
System.out.println(obj);
}
// list.add("test"); // 编译错误!不能写入(除了 null)
}💡 面试高频点:PECS 原则
┌─────────────────────────────────────────────────────────────────┐
│ PECS 原则 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Producer Extends - 如果只读取数据,用 extends │
│ ─────────────────────────────────── │
│ List<? extends Number> list = new ArrayList<Integer>(); │
│ Number num = list.get(0); // ✅ 读取安全 │
│ │
│ Consumer Super - 如果只写入数据,用 super │
│ ───────────────────────────────── │
│ List<? super Integer> list = new ArrayList<Number>(); │
│ list.add(1); // ✅ 写入安全 │
│ │
│ 如果既要读又要写,不要用通配符 │
│ │
└─────────────────────────────────────────────────────────────────┘类型擦除
什么是类型擦除?
Java 的泛型是编译时特性,运行时不保留泛型信息(向后兼容 Java 5 之前的代码)。
java
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
// 运行时会认为是同一个类
System.out.println(stringList.getClass() == intList.getClass()); // true擦除规则
┌─────────────────────────────────────────────────────────────────┐
│ 类型擦除规则 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 无限定类型参数 → 擦除为 Object │
│ public class Box<T> { } │
│ → 编译后:public class Box { Object value; } │
│ │
│ 2. 有限定类型参数 → 擦除为第一个边界类型 │
│ public class NumberBox<T extends Number> { } │
│ → 编译后:public class NumberBox { Number value; } │
│ │
│ 3. 多重限定 → 擦除为第一个满足的边界类型 │
│ T extends Comparable & Serializable │
│ → 编译后:Comparable(第一个边界) │
│ │
└─────────────────────────────────────────────────────────────────┘泛型擦除带来的问题
java
// ❌ 编译错误:无法创建泛型数组
public class GenericArray<T> {
T[] array = new T[10]; // 编译错误!
}
// ✅ 解决方案:使用反射或Object数组
public class GenericArray<T> {
T[] array;
@SuppressWarnings("unchecked")
public GenericArray(Class<T> type, int size) {
this.array = (T[]) Array.newInstance(type, size);
}
}
// ❌ 编译错误:类型参数不能用于 instanceof
if (obj instanceof List<String>) { } // 编译错误!
// ✅ 可以使用无限定通配符
if (obj instanceof List<?>) { }泛型与重载
泛型方法重载
java
// ❌ 编译错误:擦除后方法签名相同
public class Example {
public void method(List<String> list) { }
public void method(List<Integer> list) { } // 编译错误!
}
// ✅ 可以通过泛型方法重载
public class Example {
public <T> void method(List<T> list) { }
public <T> void method(T[] array) { }
}泛型的实际应用
工具类中的泛型
java
// 通用排序方法
public class SortUtils {
public static <T extends Comparable<T>> void sort(List<T> list) {
Collections.sort(list);
}
public static <T> void swap(List<T> list, int i, int j) {
T temp = list.get(i);
list.set(i, list.get(j));
list.set(j, temp);
}
}
// 通用构建器
public class Builder<T> {
private T value;
public Builder<T> with(T value) {
this.value = value;
return this;
}
public T build() {
return value;
}
}返回值泛型
java
// 通用响应对象
public class Response<T> {
private int code;
private String message;
private T data;
public static <T> Response<T> success(T data) {
Response<T> response = new Response<>();
response.code = 200;
response.data = data;
return response;
}
public static <T> Response<T> error(String message) {
Response<T> response = new Response<>();
response.code = 500;
response.message = message;
return response;
}
}
// 使用
Response<String> resp1 = Response.success("操作成功");
Response<User> resp2 = Response.success(new User());
Response<List<Order>> resp3 = Response.success(orderList);总结
泛型核心知识点:
| 知识点 | 面试频率 | 实战重要性 |
|---|---|---|
| 泛型类/接口定义 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 泛型方法 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 通配符 extends/super | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| PECS 原则 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 类型擦除 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
⚠️ 易错点提醒:
- 泛型不能直接实例化
new T() - 泛型不能用于 static 字段
- 泛型不能用于 instanceof(可以用
instanceof List<?>) List<String>不是List<Object>的子类- 通配符
? extends T不能写入,? super T不能精确读取