Skip to content
第 18 章 后端 ⏱ 10 分钟阅读

第 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。

java
List<? 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:基本类型不能做泛型参数(必须是引用类型)。

java
List<int> list;          // ❌ 编译报错
List<Integer> list;      // ✅ 用包装类

⚠️ 坑 3:不能 new 泛型数组。

java
T[] arr = new T[10];      // ❌ 编译报错
T[] arr = (T[]) new Object[10];  // ✅ 强转(有警告)

七、本章小结 ​

要点关键
泛型类class Box<T>,T 可在字段/方法中使用
泛型方法必须在返回类型前声明 <T>
? extends T上界,只能读(Producer)
? super T下界,只能写(Consumer)
基本类型不能做泛型,要用包装类
类型擦除编译后泛型信息消失,运行期是 Object

动手练习 ​

  1. 泛型 Box:实现 Pair<K, V> 类,带 getter
  2. 泛型工具方法:写 ArrayUtils.first(List<T>),返回第一个元素
  3. 通用响应体:实现上面例子的 ApiResponse<T>,写测试代码

下一章:第 19 章:文件 IO →

本站基于 VitePress 构建 · 由 StackHub 团队维护