泛型方法

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

Integer n = first(List.of(1, 2, 3));
String s = first(List.of("a", "b"));

<T> 在返回类型前声明类型参数。

泛型类

public class Box<T> {
    private T value;

    public Box(T value) { this.value = value; }
    public T get() { return value; }
    public void set(T value) { this.value = value; }
}

var b1 = new Box<Integer>(42);
var b2 = new Box<>("hi");    // 菱形运算符 <>,自动推断

<> "diamond"——Java 7+,省去重复类型。

多类型参数

public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K k, V v) { this.key = k; this.value = v; }
    public K getKey() { return key; }
    public V getValue() { return value; }
}

var p = new Pair<String, Integer>("age", 30);

约定:单字母大写——T(type)、E(element)、K(key)、V(value)、R(result)、S / U / V(额外)。

约束(bounded type parameter)

// T 必须是 Number 或子类
public static <T extends Number> double sum(List<T> list) {
    double total = 0;
    for (T n : list) total += n.doubleValue();
    return total;
}

// 多约束
public static <T extends Number & Comparable<T>> T maxOf(List<T> list) {
    T max = list.get(0);
    for (T n : list) if (n.compareTo(max) > 0) max = n;
    return max;
}

extends 在泛型里既表示"继承"也表示"实现"——别和类继承的 extends 混。

通配符 ?

List<?> anyList;            // 未知类型,能读 Object,不能 add(除 null)
List<? extends Number> nums;  // 某种 Number 子类
List<? super Integer> sink;   // Integer 的某个父类

? extends T(PECS:Producer Extends)

public static double sum(List<? extends Number> list) {
    double total = 0;
    for (Number n : list) total += n.doubleValue();
    return total;
}

sum(List.of(1, 2, 3));       // ✅ List<Integer> 是 List<? extends Number>
sum(List.of(1.0, 2.0));      // ✅ List<Double>

读出来一定是 Number("生产 Number")。不能往里加(不知道具体是什么类型)。

? super T(PECS:Consumer Super)

public static void addNumbers(List<? super Integer> dest) {
    dest.add(1);
    dest.add(2);
}

addNumbers(new ArrayList<Integer>());
addNumbers(new ArrayList<Number>());
addNumbers(new ArrayList<Object>());

? super Integer 表示"接受 Integer 或它的父类"——能写入,但读出来只能当 Object。

PECS 口诀

Producer Extends, Consumer Super

  • 你的方法只读取集合 → ? extends T
  • 你的方法只写入集合 → ? super T
  • 既读又写 → T(具体类型)

类型擦除(type erasure)

Java 泛型在运行时信息丢失

List<Integer> nums = new ArrayList<>();
List<String> strs = new ArrayList<>();

nums.getClass() == strs.getClass();   // true!都是 ArrayList.class

编译后 List<Integer>List<String> 都变成 List——类型参数被擦除。这与 C# / C++ 的"真泛型"不同。

后果:

// ❌ 不能写
if (obj instanceof List<String>) ...    // 编译错
List<T>.class                            // 编译错
T t = new T();                           // 不能用泛型参数 new

// ✅ 解决
if (obj instanceof List<?>) ...
Class<T> klass = ...; klass.newInstance();

不能基本类型

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

历史原因——泛型擦除后所有类型参数变 Object,需要 boxed type。性能敏感时:用专门库(如 Eclipse CollectionsIntArrayList)。

泛型 + 数组互不友好

List<String>[] arr = new List<String>[10];   // ❌ 编译错
List<?>[] arr2 = new List<?>[10];             // ✅ 通配符 OK

数组运行时知道元素类型,泛型擦除——两个机制冲突。用 List 替代数组

例:通用 Result 类型

public sealed interface Result<T> permits Result.Ok, Result.Err {
    record Ok<T>(T value) implements Result<T> {}
    record Err<T>(String error) implements Result<T> {}

    static <T> Result<T> ok(T value) { return new Ok<>(value); }
    static <T> Result<T> err(String error) { return new Err<>(error); }

    default boolean isOk() { return this instanceof Ok<T>; }
}

// 用
Result<User> result = repo.find(1);
if (result instanceof Result.Ok<User> ok) {
    System.out.println(ok.value().name());
}

泛型推断的限制

var map = new HashMap<>();          // ❌ 推断成 HashMap<Object, Object>
var map = new HashMap<String, Integer>();   // ✅

<> 菱形+ var 推断不友好。显式写类型参数。

实战建议

  • 字段 / 返回类型 / 参数中的泛型写完整List<String>(不是 raw List
  • raw 类型(不带 <>)会有 unchecked 警告——只在迁移老代码时碰
  • ? 让 API 更通用(PECS)
  • 不知道用什么:开始用具体类型,发现需要更通用时再加泛型
  • 复杂泛型签名让 IDE / 编译器告诉你——尝试参数化,根据错误修

→ 下一篇 集合