Skip to content

Java泛型

泛型用于把类型作为参数传递,让代码在编译期获得更强的类型检查,减少强制类型转换。

如果要深入理解类型擦除、上界下界、PECS、泛型不变性、桥接方法、泛型和反射、JDK 7/8 差异、商业项目泛型设计和线上排查,建议继续看:Java泛型全过程原理

为什么需要泛型

没有泛型时,集合里可以放任意 Object,取出时需要强转。

java
List list = new ArrayList();
list.add("hello");
String value = (String) list.get(0);

使用泛型后:

java
List<String> list = new ArrayList<>();
list.add("hello");
String value = list.get(0);

泛型类

java
public class Box<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

泛型方法

java
public static <T> T first(List<T> list) {
    return list.get(0);
}

通配符

上界通配符

java
public void printNumbers(List<? extends Number> list) {
    for (Number number : list) {
        System.out.println(number);
    }
}

? extends Number 表示可以接收 Number 或其子类列表,适合读取。

下界通配符

java
public void addInteger(List<? super Integer> list) {
    list.add(1);
}

? super Integer 表示可以接收 Integer 或其父类列表,适合写入。

PECS原则

PECS:Producer Extends, Consumer Super。

mermaid
flowchart TD
    A["只读取数据"] --> B["? extends T"]
    C["只写入数据"] --> D["? super T"]

如果参数是生产者,只从里面取数据,用 extends;如果参数是消费者,只往里面写数据,用 super

核心原理:类型擦除

Java 泛型主要在编译期生效,运行期会发生类型擦除。

java
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();

运行时二者都是 ArrayList,泛型类型参数不会完整保留。

mermaid
flowchart TD
    A[源码中的泛型] --> B[编译期类型检查]
    B --> C[类型擦除]
    C --> D[生成普通字节码]

常见限制

  • 不能直接 new T()
  • 不能创建泛型数组。
  • 基本类型不能作为泛型参数,要使用包装类。
  • 运行时不能直接判断 List<String>List<Integer>

开发建议

  1. 集合必须写泛型类型,避免原始类型。
  2. API 返回值尽量明确泛型。
  3. 只读参数用 ? extends
  4. 只写参数用 ? super
  5. 理解类型擦除,避免依赖运行时泛型类型。