强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Java 完全指南 / 13 - 泛型:泛型类、通配符、类型擦除、PECS

13 - 泛型:泛型类、通配符、类型擦除、PECS

泛型基础

泛型类

/**
 * 泛型容器类
 * @param <T> 元素类型
 */
public class Box<T> {
    private T content;

    public Box() {}
    public Box(T content) { this.content = content; }

    public T getContent() { return content; }
    public void setContent(T content) { this.content = content; }

    @Override
    public String toString() {
        return "Box{" + content + "}";
    }
}

// 多泛型参数
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; }

    @Override
    public String toString() {
        return "(" + key + ", " + value + ")";
    }
}

// 使用
public class GenericDemo {
    public static void main(String[] args) {
        Box<String> strBox = new Box<>("Hello");
        Box<Integer> intBox = new Box<>(42);

        String s = strBox.getContent();  // 无需强转
        int i = intBox.getContent();      // 自动拆箱

        Pair<String, Integer> pair = new Pair<>("年龄", 25);
        System.out.println(pair);  // (年龄, 25)

        // 菱形推断(JDK 7+)
        Box<Double> doubleBox = new Box<>(3.14);
    }
}

泛型方法

public class GenericMethods {
    // 泛型方法 —— <T> 声明在返回类型之前
    public static <T> Box<T> wrap(T value) {
        return new Box<>(value);
    }

    // 有界泛型方法
    public static <T extends Comparable<T>> T max(T a, T b) {
        return a.compareTo(b) >= 0 ? a : b;
    }

    // 多个边界
    public static <T extends Comparable<T> & java.io.Serializable> T min(T a, T b) {
        return a.compareTo(b) <= 0 ? a : b;
    }

    public static void main(String[] args) {
        Box<String> box = wrap("hello");     // 类型推断
        Box<List<Integer>> listBox = wrap(List.of(1, 2, 3));

        System.out.println(max(3, 5));        // 5
        System.out.println(max("abc", "xyz")); // xyz
    }
}

泛型通配符

无界通配符 <?>

// 接受任意类型
public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

// 只读操作适用
public static int size(List<?> list) {
    return list.size();
}

上界通配符 <? extends T>(生产者)

// 接受 T 或 T 的子类
public static double sum(List<? extends Number> list) {
    double total = 0;
    for (Number n : list) {  // 可以读取为 Number
        total += n.doubleValue();
    }
    // list.add(1);  // ❌ 编译错误!不能写入
    return total;
}

// 使用
List<Integer> ints = List.of(1, 2, 3);
List<Double> doubles = List.of(1.1, 2.2, 3.3);
System.out.println(sum(ints));    // 6.0
System.out.println(sum(doubles)); // 6.6

下界通配符 <? super T>(消费者)

// 接受 T 或 T 的父类
public static void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
    // Integer i = list.get(0);  // ❌ 编译错误!只能读取为 Object
    Object obj = list.get(0);     // ✅ 可以读取为 Object
}

// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);  // List<Number> 可以接受 Integer
List<Object> objects = new ArrayList<>();
addNumbers(objects);  // List<Object> 也可以

PECS 原则

Producer Extends, Consumer Super —— 生产者用 extends,消费者用 super

场景通配符说明
只读(生产数据)? extends T可以安全地读取为 T
只写(消费数据)? super T可以安全地写入 T
读写都做确定类型 T不使用通配符
public class PECSExample {
    // 从 src 读取,写入 dest
    public static <T> void copy(
            List<? extends T> src,    // 生产者:从这里读取
            List<? super T> dest) {   // 消费者:往这里写入
        for (T item : src) {
            dest.add(item);
        }
    }

    public static void main(String[] args) {
        List<Integer> source = List.of(1, 2, 3);
        List<Number> dest = new ArrayList<>();

        copy(source, dest);  // Integer extends Number
        System.out.println(dest);  // [1, 2, 3]
    }
}

类型擦除

public class TypeErasureDemo {
    public static void main(String[] args) {
        // 泛型在编译后被擦除
        List<String> strings = new ArrayList<>();
        List<Integer> integers = new ArrayList<>();

        // 运行时类型相同!
        System.out.println(strings.getClass() == integers.getClass());  // true

        // 反射可以绕过泛型检查
        integers.getClass().getMethod("add", Object.class)
                .invoke(integers, "不是数字!");  // 运行时不报错
        System.out.println(integers);  // [不是数字!] —— 类型安全被破坏
    }
}

// 泛型的限制
public class ErasureLimitations {
    // ❌ 不能用基本类型
    // Box<int> box = new Box<>();  // 编译错误,用 Box<Integer>

    // ❌ 不能 instanceof 泛型
    // if (obj instanceof List<String>) {}  // 编译错误
    public static <T> boolean isStringList(Object obj) {
        return obj instanceof List<?>;  // 只能检查原始类型
    }

    // ❌ 不能创建泛型数组
    // T[] arr = new T[10];  // 编译错误
    public static <T> T[] createArray(int size, Class<T> componentType) {
        @SuppressWarnings("unchecked")
        T[] arr = (T[]) java.lang.reflect.Array.newInstance(componentType, size);
        return arr;
    }
}

泛型的实际应用

// 数据仓库接口
public interface Repository<T, ID> {
    T findById(ID id);
    List<T> findAll();
    T save(T entity);
    void delete(ID id);
}

// 事件系统
public class EventBus {
    private final Map<Class<?>, List<Consumer<?>>> handlers = new HashMap<>();

    public <T> void register(Class<T> eventType, Consumer<T> handler) {
        handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
    }

    @SuppressWarnings("unchecked")
    public <T> void publish(T event) {
        List<Consumer<?>> list = handlers.get(event.getClass());
        if (list != null) {
            for (Consumer<?> handler : list) {
                ((Consumer<T>) handler).accept(event);
            }
        }
    }
}

// Builder 模式
public class QueryBuilder<T> {
    private final Class<T> entityClass;
    private final List<String> conditions = new ArrayList<>();
    private int limit = 100;

    public QueryBuilder(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public QueryBuilder<T> where(String condition) {
        conditions.add(condition);
        return this;
    }

    public QueryBuilder<T> limit(int limit) {
        this.limit = limit;
        return this;
    }
}

⚠️ 注意事项

  1. 泛型不支持基本类型 — 必须使用包装类:List<Integer> 而非 List<int>
  2. 类型擦除后无法区分 List<String>List<Integer> — 运行时都是 List
  3. 不要创建泛型数组 — 使用 List<T> 代替,或用 Array.newInstance()
  4. 通配符捕获 — 编译器不能证明安全性时会报错。

💡 技巧

  1. 钻石推断var list = new ArrayList<String>(); 简化声明。
  2. @SuppressWarnings("unchecked") — 确认安全后抑制警告,加注释说明原因。
  3. Collections.checkedList() — 运行时类型检查,调试时有用。

🏢 业务场景

  • DAO 层: Repository<T, ID> 统一数据访问接口。
  • 工具类: Collections.sort(List<T>) 适用于任何可比较类型。
  • API 返回值: ResponseEntity<T> 封装 HTTP 响应。
  • 缓存: Cache<K, V> 泛型缓存实现。

📖 扩展阅读