泛型方法
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 Collections 的 IntArrayList)。
泛型 + 数组互不友好
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>(不是 rawList) - raw 类型(不带
<>)会有 unchecked 警告——只在迁移老代码时碰 - 用
?让 API 更通用(PECS) - 不知道用什么:开始用具体类型,发现需要更通用时再加泛型
- 复杂泛型签名让 IDE / 编译器告诉你——尝试参数化,根据错误修
→ 下一篇 集合