第 18 章:泛型
学习目标
- 掌握泛型类、泛型方法的基本使用
- 学会常见通配符的用法
- 避开泛型的常见坑
一、为什么需要泛型?
泛型 = 让类/方法支持多种类型,编译期检查类型安全。
java
// ❌ 没有泛型:每次强转,还能塞错类型
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 必须强转
list.add(123); // ❌ 可以混入 Integer,运行时才炸
// ✅ 有泛型:编译期就拦住
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); // ❌ 编译就报错
String s = list.get(0); // 无需强转,自动推断二、泛型类
java
public class Box<T> { // <T> 是类型参数
private T content;
public void put(T item) {
this.content = item;
}
public T get() {
return content;
}
}
Box<String> stringBox = new Box<>();
stringBox.put("hello");
String s = stringBox.get(); // s = "hello"
Box<Integer> intBox = new Box<>();
intBox.put(123);
Integer i = intBox.get(); // i = 123多类型参数(类似 Map.Entry):
java
public class Pair<K, V> {
private final K key;
private final 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", 18);三、泛型方法
java
public class ArrayUtils {
// ① 返回类型前声明 <T>
public static <T> T getFirst(T[] arr) {
return arr[0];
}
public static <T> void swap(T[] arr, int i, int j) {
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
String[] strs = {"A", "B", "C"};
String first = ArrayUtils.getFirst(strs); // "A"
ArrayUtils.swap(strs, 0, 2); // [C, B, A]四、通配符(常用)
java
List<?> unbounded; // 任意类型
List<? extends Number> upperBounded; // Number 或其子类(Integer、Double)
List<? super Integer> lowerBounded; // Integer 或其父类(Number、Object)PECS 原则:
| 场景 | 用 |
|---|---|
| 只读(Producer) | <? extends T> |
| 只写(Consumer) | <? super T> |
java
// 只读:从 src 拷贝到 dest
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
for (T item : src) {
dest.add(item);
}
}⚠️ 坑 1:
List<? extends Number>不能 add——编译期不知道具体是 Integer 还是 Double。javaList<? extends Number> list = new ArrayList<Integer>(); list.add(1); // ❌ 编译报错
五、泛型实战:通用响应体
java
public class ApiResponse<T> {
private final int code;
private final String message;
private final T data;
public ApiResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(0, "成功", data);
}
public static <T> ApiResponse<T> error(int code, String msg) {
return new ApiResponse<>(code, msg, null);
}
// getter
}
// 使用
ApiResponse<User> resp = ApiResponse.success(user);
ApiResponse<List<Order>> orders = ApiResponse.success(orderList);六、泛型的限制
⚠️ 坑 2:基本类型不能做泛型参数(必须是引用类型)。
javaList<int> list; // ❌ 编译报错 List<Integer> list; // ✅ 用包装类
⚠️ 坑 3:不能 new 泛型数组。
javaT[] arr = new T[10]; // ❌ 编译报错 T[] arr = (T[]) new Object[10]; // ✅ 强转(有警告)
七、本章小结
| 要点 | 关键 |
|---|---|
| 泛型类 | class Box<T>,T 可在字段/方法中使用 |
| 泛型方法 | 必须在返回类型前声明 <T> |
? extends T | 上界,只能读(Producer) |
? super T | 下界,只能写(Consumer) |
| 基本类型 | 不能做泛型,要用包装类 |
| 类型擦除 | 编译后泛型信息消失,运行期是 Object |
动手练习
- 泛型 Box:实现
Pair<K, V>类,带 getter - 泛型工具方法:写
ArrayUtils.first(List<T>),返回第一个元素 - 通用响应体:实现上面例子的
ApiResponse<T>,写测试代码
下一章:第 19 章:文件 IO →