13 泛型¶
本章目标¶
- 理解泛型:让代码支持多种类型且安全
- 掌握泛型类、泛型方法
- 掌握通配符 ? extends / ? super
- 了解类型擦除
13.1 什么是泛型?¶
泛型(Generic) 就是"类型参数化":写代码时用占位符 T,使用时指定具体类型。
// 一个箱子类,什么类型都能装,且安全
public class Box<T> {
private T content;
public void put(T content) {
this.content = content;
}
public T get() {
return content;
}
}
// 使用
Box<String> stringBox = new Box<>();
stringBox.put("宝剑");
String sword = stringBox.get(); // 不用强转,类型安全
Box<Integer> intBox = new Box<>();
intBox.put(100);
int value = intBox.get();
如果不写泛型(Object),取出来全是 Object,要强转,还容易转错:
Box box = new Box(); // 裸类型
box.put("hello");
box.put(123); // 什么都能放
Integer n = (Integer) box.get(); // ❌ 强转出错 ClassCastException
13.2 泛型类¶
public class Pair<K, V> { // 两个类型参数
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
// 使用
Pair<String, Integer> p = new Pair<>("level", 30);
13.3 泛型方法¶
public class Utils {
// 泛型方法:<T> 写在返回值前
public static <T> T firstOrNull(List<T> list) {
return list.isEmpty() ? null : list.get(0);
}
public static <T> List<T> copyToList(T... items) {
return new ArrayList<>(List.of(items));
}
}
// 调用(类型自动推断)
String s = Utils.firstOrNull(List.of("a", "b"));
Integer i = Utils.firstOrNull(List.of(1, 2, 3));
13.4 通配符 ?¶
只读上界:? extends T¶
"这是 T 或 T 的子类的集合",可以读,不能往里放:
static double totalArea(List<? extends Shape> shapes) {
double sum = 0;
for (Shape s : shapes) { // 可以读,按 Shape 处理
sum += s.area();
}
return sum;
}
List<Circle> circles = new ArrayList<>();
totalArea(circles); // ✅ List<Circle> 能传进来
只写下界:? super T¶
"这是 T 或 T 的父类的集合",可以放 T,读出来是 Object:
static void addNumbers(List<? super Integer> list) {
list.add(1); // ✅ 可以放 Integer
list.add(2);
}
List<Number> nums = new ArrayList<>();
addNumbers(nums); // ✅
记忆口诀:PECS
Producer Extends,Consumer Super——生产者用 extends(只读),消费者用 super(只写)。
13.5 类型擦除(Type Erasure)¶
泛型是编译期的概念。编译后 Box<String> 和 Box<Integer> 是同一个类,类型信息被擦除:
Box<String> a = new Box<>();
Box<Integer> b = new Box<>();
a.getClass() == b.getClass(); // true!运行时都是 Box.class
由此带来几个限制:
- 不能 new T():
new T()编译错误(运行时不知道 T 是什么) - 不能创建泛型数组:
T[] arr = new T[10]编译错误 - 不能 instanceof T:
obj instanceof T编译错误
public class Container<T> {
public void create() {
// new T(); // ❌ 编译错误
}
}
13.6 实战:模组场景¶
// 简化的方块实体数据缓存:按类型存数据
public class DataCache<K, V> {
private final Map<K, V> cache = new HashMap<>();
public void set(K key, V value) { cache.put(key, value); }
public V get(K key) { return cache.get(key); }
public V getOrDefault(K key, V def) { return cache.getOrDefault(key, def); }
}
// 使用
DataCache<String, Integer> blockStates = new DataCache<>();
blockStates.set("furnace:lit", 1);
13.7 小练习¶
- 写泛型类
Stack<T>:push / pop / isEmpty - 写泛型方法
swap(List<T> list, int i, int j)交换两个元素 - 写方法
printAll(List<? extends Number>),打印所有数字
本章小结¶
- 泛型 = 类型参数化,编译期检查,运行期擦除
- 泛型类/泛型方法语法:
<T>占位 - 通配符:
? extends T只读、? super T只写(PECS) - 不能 new T、不能泛型数组、不能 instanceof T
下一章:14 内部类与匿名类