Java 泛型详解
深入解析 Java 泛型机制,涵盖类型参数、通配符与类型擦除原理
目录
泛型概述
泛型(Generics)是 Java 5 引入的参数化类型机制,允许在定义类、接口和方法时使用类型参数,从而实现代码的复用和类型安全。
泛型类
// 定义泛型类
public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
// 使用
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String value = stringBox.getContent(); // 无需强制类型转换泛型方法
public class Util {
// 泛型方法
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
// 有界类型参数
public static <T extends Comparable<T>> T findMax(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
}通配符
无边界的通配符
List<?> unknownList = new ArrayList<String>();
// 可以读取,但不能添加(除 null 外)
Object obj = unknownList.get(0);
// unknownList.add("test"); // 编译错误上界通配符 <? extends T>
// 可以读取 Number 及其子类
public double sum(List<? extends Number> numbers) {
double total = 0;
for (Number num : numbers) {
total += num.doubleValue();
}
return total;
}
// 使用
sum(Arrays.asList(1, 2, 3)); // Integer
sum(Arrays.asList(1.5, 2.5)); // Double下界通配符 <? super T>
// 可以添加 Integer 及其父类
public void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
}
// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);PECS 原则
Producer Extends, Consumer Super
| 角色 | 通配符 | 说明 |
|---|---|---|
| Producer(生产者) | ? extends T | 提供数据,只能读取 |
| Consumer(消费者) | ? super T | 接收数据,只能写入 |
// Producer - 从列表读取数据
public void copyFrom(List<? extends T> src, List<T> dest) {
for (T item : src) {
dest.add(item);
}
}
// Consumer - 向列表写入数据
public void copyTo(List<T> src, List<? super T> dest) {
for (T item : src) {
dest.add(item);
}
}类型擦除
泛型在编译后会被擦除,运行时无法获取类型参数信息。
// 编译前
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
// 编译后(类型擦除)
List stringList = new ArrayList();
List intList = new ArrayList();
// 运行时
System.out.println(stringList.getClass() == intList.getClass()); // true桥接方法
class Node<T> {
public void setData(T data) { }
}
class MyNode extends Node<Integer> {
// 编译器生成桥接方法
// public void setData(Object data) { setData((Integer) data); }
public void setData(Integer data) { }
}泛型的限制
不能实例化类型参数
// 错误 T instance = new T(); T[] array = new T[10]; // 正确 T instance = (T) new Object(); T[] array = (T[]) new Object[10];不能使用基本类型
// 错误 List<int> list; // 正确 List<Integer> list;不能创建泛型数组
// 错误 List<String>[] array = new List<String>[10]; // 正确 List<?>[] array = new List<?>[10];不能捕获泛型类型的异常
// 错误 try { } catch (T e) { }
泛型与继承
// Integer 是 Number 的子类
// 但 List<Integer> 不是 List<Number> 的子类
List<Integer> intList = new ArrayList<>();
// List<Number> numList = intList; // 编译错误
// 使用通配符实现协变
List<? extends Number> covariant = intList; // OK实际应用
类型安全的 DAO 模式
public interface GenericDao<T, ID> {
T findById(ID id);
List<T> findAll();
void save(T entity);
void delete(ID id);
}
public class UserDao implements GenericDao<User, Long> {
// 实现...
}函数式接口(Java 8)
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
// 使用
Predicate<String> isEmpty = String::isEmpty;总结
- 类型安全:编译期检查类型,避免运行时 ClassCastException
- 代码复用:一个泛型类/方法可适用于多种类型
- 消除强制类型转换:获取数据时无需手动转换
- PECS 原则:生产者用 extends,消费者用 super
- 类型擦除:泛型信息在运行时不可用,仅存在于编译期