mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
710 字
2 分钟
Java泛型编程详解
2026-02-02

泛型刚出来的时候我觉得不就是个类型参数嘛,有啥好学的。后来被PECS原则(Producer Extends Consumer Super)绕晕了,才明白泛型的水有多深。这篇文章把我对泛型的理解整理了一遍,希望能帮你少走弯路。

Java泛型编程详解#

什么是泛型?#

泛型是Java 5引入的特性,允许在定义类、接口和方法时使用类型参数,实现代码复用和类型安全。在Java 5之前,从集合里拿东西都要强转,写起来又丑又不安全,泛型出来之后整个世界都清爽了。

基本语法#

泛型类#

public class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
// 使用
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
String value = stringBox.getValue();

泛型方法#

public class GenericMethod {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
}
}

类型参数命名约定#

  • T:Type(类型)
  • E:Element(元素,集合用)
  • KV:Key、Value(Map用)
  • N:Number(数字)

通配符与边界#

上界通配符 ? extends T#

表示类型参数是T的子类或T本身。只能读取,不能写入(除了null)。

public static double sum(List<? extends Number> numbers) {
double sum = 0.0;
for (Number n : numbers) sum += n.doubleValue();
return sum;
}
// 可以传 List<Integer>、List<Double> 等

下界通配符 ? super T#

表示类型参数是T的父类或T本身。只能写入,读取时只能读到Object

public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
// 可以传 List<Number>、List<Object> 等

无界通配符 ?#

表示任意类型,但只能读不能写(除了null)。

public static void printList(List<?> list) {
for (Object element : list) {
System.out.print(element + " ");
}
}

PECS 原则#

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

  • 如果你要从集合中读取数据(生产者),用 ? extends T
  • 如果你要向集合中写入数据(消费者),用 ? super T
  • 如果既要读又要写,就别用通配符,直接用具体类型

这个原则是理解泛型通配符的核心,面试常考,建议背下来。

类型擦除#

Java的泛型是编译时机制,运行时类型信息会被擦除。比如:

// 编译时
Box<String> box = new Box<>();
// 编译后(擦除后)
Box box = new Box(); // 相当于原始类型

类型擦除带来的限制:

  1. 不能创建泛型数组new List<String>[10] 编译不过
  2. 不能使用instanceof检查泛型类型box instanceof Box<String> 编译不过,只能 box instanceof Box<?>
  3. 不能使用基本类型作为类型参数Box<int> 不行,要用 Box<Integer>
  4. 不能在静态上下文中使用类型参数

常用陷阱#

  1. 原始类型——现在写代码看到没用泛型的集合,心里就发毛,因为永远不知道get出来的是什么类型
  2. 通配符用错——该用extends的地方用了super,编译不过
  3. 类型擦除的误解——运行时拿不到泛型类型信息,别指望用反射去获取

💡 实战贴士: 写工具类方法时,如果参数是集合且只读,用 ? extends T;如果只写,用 ? super T。举个实际例子:Collections.sort(List<T> list) 的签名是 public static <T extends Comparable<? super T>> void sort(List<T> list),这里的 ? super T 就是PECS的应用——因为Comparable是”消费”T的。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

部分信息可能已经过时

目录