目录

Java 泛型详解

深入解析 Java 泛型机制,涵盖类型参数、通配符与类型擦除原理

泛型概述

泛型(Generics)是 Java 5 引入的参数化类型机制,允许在定义类、接口和方法时使用类型参数,从而实现代码的复用和类型安全。

泛型类

// 定义泛型类
public class Box<T> {
    private T content;

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

    public T getContent() {
        return content;
    }
}

// 使用
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String value = stringBox.getContent(); // 无需强制类型转换

泛型方法

public class Util {
    // 泛型方法
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    // 有界类型参数
    public static <T extends Comparable<T>> T findMax(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}

通配符

无边界的通配符

List<?> unknownList = new ArrayList<String>();
// 可以读取,但不能添加(除 null 外)
Object obj = unknownList.get(0);
// unknownList.add("test"); // 编译错误

上界通配符 <? extends T>

// 可以读取 Number 及其子类
public double sum(List<? extends Number> numbers) {
    double total = 0;
    for (Number num : numbers) {
        total += num.doubleValue();
    }
    return total;
}

// 使用
sum(Arrays.asList(1, 2, 3));      // Integer
sum(Arrays.asList(1.5, 2.5));     // Double

下界通配符 <? super T>

// 可以添加 Integer 及其父类
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);

PECS 原则

Producer Extends, Consumer Super

角色通配符说明
Producer(生产者)? extends T提供数据,只能读取
Consumer(消费者)? super T接收数据,只能写入
// Producer - 从列表读取数据
public void copyFrom(List<? extends T> src, List<T> dest) {
    for (T item : src) {
        dest.add(item);
    }
}

// Consumer - 向列表写入数据
public void copyTo(List<T> src, List<? super T> dest) {
    for (T item : src) {
        dest.add(item);
    }
}

类型擦除

泛型在编译后会被擦除,运行时无法获取类型参数信息。

// 编译前
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();

// 编译后(类型擦除)
List stringList = new ArrayList();
List intList = new ArrayList();

// 运行时
System.out.println(stringList.getClass() == intList.getClass()); // true

桥接方法

class Node<T> {
    public void setData(T data) { }
}

class MyNode extends Node<Integer> {
    // 编译器生成桥接方法
    // public void setData(Object data) { setData((Integer) data); }
    public void setData(Integer data) { }
}

泛型的限制

  1. 不能实例化类型参数

    // 错误
    T instance = new T();
    T[] array = new T[10];
    
    // 正确
    T instance = (T) new Object();
    T[] array = (T[]) new Object[10];
  2. 不能使用基本类型

    // 错误
    List<int> list;
    
    // 正确
    List<Integer> list;
  3. 不能创建泛型数组

    // 错误
    List<String>[] array = new List<String>[10];
    
    // 正确
    List<?>[] array = new List<?>[10];
  4. 不能捕获泛型类型的异常

    // 错误
    try { } catch (T e) { }

泛型与继承

// Integer 是 Number 的子类
// 但 List<Integer> 不是 List<Number> 的子类

List<Integer> intList = new ArrayList<>();
// List<Number> numList = intList; // 编译错误

// 使用通配符实现协变
List<? extends Number> covariant = intList; // OK

实际应用

类型安全的 DAO 模式

public interface GenericDao<T, ID> {
    T findById(ID id);
    List<T> findAll();
    void save(T entity);
    void delete(ID id);
}

public class UserDao implements GenericDao<User, Long> {
    // 实现...
}

函数式接口(Java 8)

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

// 使用
Predicate<String> isEmpty = String::isEmpty;

总结

  1. 类型安全:编译期检查类型,避免运行时 ClassCastException
  2. 代码复用:一个泛型类/方法可适用于多种类型
  3. 消除强制类型转换:获取数据时无需手动转换
  4. PECS 原则:生产者用 extends,消费者用 super
  5. 类型擦除:泛型信息在运行时不可用,仅存在于编译期