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>。
开发建议
- 集合必须写泛型类型,避免原始类型。
- API 返回值尽量明确泛型。
- 只读参数用
? extends。 - 只写参数用
? super。 - 理解类型擦除,避免依赖运行时泛型类型。
