13 - 泛型:泛型类、通配符、类型擦除、PECS
13 - 泛型:泛型类、通配符、类型擦除、PECS
泛型基础
泛型类
/**
* 泛型容器类
* @param <T> 元素类型
*/
public class Box<T> {
private T content;
public Box() {}
public Box(T content) { this.content = content; }
public T getContent() { return content; }
public void setContent(T content) { this.content = content; }
@Override
public String toString() {
return "Box{" + content + "}";
}
}
// 多泛型参数
public class Pair<K, V> {
private final K key;
private final V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
@Override
public String toString() {
return "(" + key + ", " + value + ")";
}
}
// 使用
public class GenericDemo {
public static void main(String[] args) {
Box<String> strBox = new Box<>("Hello");
Box<Integer> intBox = new Box<>(42);
String s = strBox.getContent(); // 无需强转
int i = intBox.getContent(); // 自动拆箱
Pair<String, Integer> pair = new Pair<>("年龄", 25);
System.out.println(pair); // (年龄, 25)
// 菱形推断(JDK 7+)
Box<Double> doubleBox = new Box<>(3.14);
}
}
泛型方法
public class GenericMethods {
// 泛型方法 —— <T> 声明在返回类型之前
public static <T> Box<T> wrap(T value) {
return new Box<>(value);
}
// 有界泛型方法
public static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
// 多个边界
public static <T extends Comparable<T> & java.io.Serializable> T min(T a, T b) {
return a.compareTo(b) <= 0 ? a : b;
}
public static void main(String[] args) {
Box<String> box = wrap("hello"); // 类型推断
Box<List<Integer>> listBox = wrap(List.of(1, 2, 3));
System.out.println(max(3, 5)); // 5
System.out.println(max("abc", "xyz")); // xyz
}
}
泛型通配符
无界通配符 <?>
// 接受任意类型
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
// 只读操作适用
public static int size(List<?> list) {
return list.size();
}
上界通配符 <? extends T>(生产者)
// 接受 T 或 T 的子类
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number n : list) { // 可以读取为 Number
total += n.doubleValue();
}
// list.add(1); // ❌ 编译错误!不能写入
return total;
}
// 使用
List<Integer> ints = List.of(1, 2, 3);
List<Double> doubles = List.of(1.1, 2.2, 3.3);
System.out.println(sum(ints)); // 6.0
System.out.println(sum(doubles)); // 6.6
下界通配符 <? super T>(消费者)
// 接受 T 或 T 的父类
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
// Integer i = list.get(0); // ❌ 编译错误!只能读取为 Object
Object obj = list.get(0); // ✅ 可以读取为 Object
}
// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers); // List<Number> 可以接受 Integer
List<Object> objects = new ArrayList<>();
addNumbers(objects); // List<Object> 也可以
PECS 原则
Producer Extends, Consumer Super —— 生产者用 extends,消费者用 super
| 场景 | 通配符 | 说明 |
|---|---|---|
| 只读(生产数据) | ? extends T | 可以安全地读取为 T |
| 只写(消费数据) | ? super T | 可以安全地写入 T |
| 读写都做 | 确定类型 T | 不使用通配符 |
public class PECSExample {
// 从 src 读取,写入 dest
public static <T> void copy(
List<? extends T> src, // 生产者:从这里读取
List<? super T> dest) { // 消费者:往这里写入
for (T item : src) {
dest.add(item);
}
}
public static void main(String[] args) {
List<Integer> source = List.of(1, 2, 3);
List<Number> dest = new ArrayList<>();
copy(source, dest); // Integer extends Number
System.out.println(dest); // [1, 2, 3]
}
}
类型擦除
public class TypeErasureDemo {
public static void main(String[] args) {
// 泛型在编译后被擦除
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// 运行时类型相同!
System.out.println(strings.getClass() == integers.getClass()); // true
// 反射可以绕过泛型检查
integers.getClass().getMethod("add", Object.class)
.invoke(integers, "不是数字!"); // 运行时不报错
System.out.println(integers); // [不是数字!] —— 类型安全被破坏
}
}
// 泛型的限制
public class ErasureLimitations {
// ❌ 不能用基本类型
// Box<int> box = new Box<>(); // 编译错误,用 Box<Integer>
// ❌ 不能 instanceof 泛型
// if (obj instanceof List<String>) {} // 编译错误
public static <T> boolean isStringList(Object obj) {
return obj instanceof List<?>; // 只能检查原始类型
}
// ❌ 不能创建泛型数组
// T[] arr = new T[10]; // 编译错误
public static <T> T[] createArray(int size, Class<T> componentType) {
@SuppressWarnings("unchecked")
T[] arr = (T[]) java.lang.reflect.Array.newInstance(componentType, size);
return arr;
}
}
泛型的实际应用
// 数据仓库接口
public interface Repository<T, ID> {
T findById(ID id);
List<T> findAll();
T save(T entity);
void delete(ID id);
}
// 事件系统
public class EventBus {
private final Map<Class<?>, List<Consumer<?>>> handlers = new HashMap<>();
public <T> void register(Class<T> eventType, Consumer<T> handler) {
handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
}
@SuppressWarnings("unchecked")
public <T> void publish(T event) {
List<Consumer<?>> list = handlers.get(event.getClass());
if (list != null) {
for (Consumer<?> handler : list) {
((Consumer<T>) handler).accept(event);
}
}
}
}
// Builder 模式
public class QueryBuilder<T> {
private final Class<T> entityClass;
private final List<String> conditions = new ArrayList<>();
private int limit = 100;
public QueryBuilder(Class<T> entityClass) {
this.entityClass = entityClass;
}
public QueryBuilder<T> where(String condition) {
conditions.add(condition);
return this;
}
public QueryBuilder<T> limit(int limit) {
this.limit = limit;
return this;
}
}
⚠️ 注意事项
- 泛型不支持基本类型 — 必须使用包装类:
List<Integer>而非List<int>。 - 类型擦除后无法区分
List<String>和List<Integer>— 运行时都是List。 - 不要创建泛型数组 — 使用
List<T>代替,或用Array.newInstance()。 - 通配符捕获 — 编译器不能证明安全性时会报错。
💡 技巧
- 钻石推断 —
var list = new ArrayList<String>();简化声明。 @SuppressWarnings("unchecked")— 确认安全后抑制警告,加注释说明原因。Collections.checkedList()— 运行时类型检查,调试时有用。
🏢 业务场景
- DAO 层:
Repository<T, ID>统一数据访问接口。 - 工具类:
Collections.sort(List<T>)适用于任何可比较类型。 - API 返回值:
ResponseEntity<T>封装 HTTP 响应。 - 缓存:
Cache<K, V>泛型缓存实现。