一、一句话理解
泛型 = 类型参数化,让代码更安全、可复用
二、核心语法
1. 类泛型
// 定义
class Box<T> { // T: 类型参数
private T content;
void set(T content) { this.content = content; }
T get() { return content; }
}
// 使用
Box<String> box = new Box<>(); // 具体类型:String
box.set("Hello");
String s = box.get(); // 无需强制转换
2. 方法泛型
// 定义
<T> T getFirst(List<T> list) { // <T>在返回值前
return list.get(0);
}
// 使用
String first = getFirst(Arrays.asList("A", "B")); // 自动推断T为String
3. 接口泛型
// 定义
interface Pair<K, V> { // 多个类型参数
K getKey();
V getValue();
}
// 实现
class StringIntegerPair implements Pair<String, Integer> {
public String getKey() { return "age"; }
public Integer getValue() { return 25; }
}
三、类型通配符
1. ? 无界通配符
// 接收任何类型的List
void printList(List<?> list) { // ?表示任意类型
for (Object elem : list) {
System.out.println(elem);
}
}
2. ? extends T 上界通配符
// 接收T及其子类型的List
void processNumbers(List<? extends Number> list) { // Number或其子类
// 可以读取为Number
Number n = list.get(0);
// ❌ 不能添加(不知道具体子类型)
// list.add(new Integer(1)); // 编译错误
}
3. ? super T 下界通配符
// 接收T及其父类型的List
void addNumbers(List<? super Integer> list) { // Integer或其父类
// 可以添加Integer及其子类
list.add(10);
// ❌ 读取时只能得到Object
Object obj = list.get(0);
}
四、关键限制
1. 类型擦除
// 编译时:Box<String>
// 运行时:Box<Object>(类型信息被擦除)
Box<String> box1 = new Box<>();
Box<Integer> box2 = new Box<>();
System.out.println(box1.getClass() == box2.getClass()); // true
2. 不能做的事
// ❌ 不能创建泛型数组
T[] array = new T[10]; // 错误
// ❌ 不能实例化类型参数
T obj = new T(); // 错误
// ❌ 不能用于静态上下文
class Box<T> {
// static T content; // 错误
}
五、实际应用
1. 集合框架
// 没有泛型(JDK 1.5前)
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 需要强制转换
// 有泛型
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 自动类型安全
2. 工具方法
// 安全地交换数组元素
static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 找出最大值
static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
六、记忆口诀
-
PECS原则(Producer Extends, Consumer Super)
- 生产者用
extends(只能取) - 消费者用
super(只能存)
- 生产者用
-
使用时机:
- 类/方法要处理多种类型 → 用泛型
- 要保证类型安全 → 用泛型
- 避免强制类型转换 → 用泛型
七、简单示例
// 1. 泛型类
class Container<T> {
private T item;
public void put(T item) { this.item = item; }
public T get() { return item; }
}
// 2. 泛型方法
class Utils {
public static <T> boolean isEqual(T a, T b) {
return a.equals(b);
}
}
// 3. 泛型接口
interface Transformer<T, R> {
R transform(T input);
}
总结
泛型 = 让编译器帮我们检查类型安全,减少运行时错误,让代码更通用。