你是否在Java泛型中见过这些符号:T、?、K/V、extends、super?它们看似简单,却是泛型的核心!泛型不仅是类型安全的守护者,更是代码复用和灵活性的利器。本文将用代码实战带你彻底搞懂它们!
一、基础符号:TvsK/V
1.T:类型占位符
- 作用:定义类/方法时表明具体类型的占位符。
- 场景:泛型类、泛型方法。
// 定义泛型类
public class Box<T> {
private T data;
public void setData(T data) { this.data = data; }
public T getData() { return data; }
}
// 使用
Box<String> stringBox = new Box<>();
stringBox.setData("Hello");
String value = stringBox.getData(); // 直接获取String,无需强转
2.K/V:键值对专用
- 作用:K表明键(Key),V表明值(Value),是T的特殊化。
- 场景:Map、键值对场景。
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
// getters...
}
// 使用
Pair<String, Integer> user = new Pair<>("age", 30);
String key = user.getKey(); // String类型
Integer value = user.getValue(); // Integer类型
二、通配符?:灵活但受限
1. 基础通配符
- 作用:表明未知类型,增强灵活性。
- 限制:只能读(返回Object),不能写(除null外)。
public void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem); // 可读取
}
// list.add("error"); // 编译错误!不能写入
}
三、边界限定:extends vs super
1.? extends T:生产者(Producer)
- 作用:接受T或其子类,安全读取数据。
- 口诀:Producer Extends(生产者提供数据)。
// 计算任意Number子类列表的总和
public double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue(); // 安全读取
}
return total;
}
// 使用
List<Integer> ints = List.of(1, 2, 3);
sum(ints); // 合法!Integer是Number的子类
2.? super T:消费者(Consumer)
- 作用:接受T或其父类,安全写入数据。
- 口诀:Consumer Super(消费者消费数据)。
// 将数字写入任意Integer父类的列表
public void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 3; i++) {
list.add(i); // 安全写入
}
}
// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers); // 合法!Number是Integer的父类
四、对比总结:核心区别表
|
符号 |
使用场景 |
数据方向 |
示例 |
|
T |
定义泛型类/方法 |
读写 |
Box<T> |
|
K/V |
键值对 |
读写 |
Pair<K,V> |
|
? |
未知类型 |
只读 |
List<?> |
|
? extends T |
读取数据(生产者) |
只读 |
List<? extends Number> |
|
? super T |
写入数据(消费者) |
只写 |
List<? super Integer> |
五、实战应用:PECS原则
PECS(Producer-Extends, Consumer-Super) 是泛型使用的黄金法则:
// Java集合框架的copy方法
public static <T> void copy(
List<? super T> dest, // 消费者:写入目标
List<? extends T> src // 生产者:读取源
) {
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i)); // 从src读,向dest写
}
}
// 使用
List<Integer> src = List.of(1, 2, 3);
List<Number> dest = new ArrayList<>();
Collections.copy(dest, src); // 安全复制!
六、为什么泛型如此重大?
- 类型安全:避免ClassCastException。
- 代码复用:一套逻辑处理多种类型。
- 代码清晰:减少强制类型转换。
结语
掌握泛型符号的区别,是写出高质量Java代码的关键!记住:
- T/K/V用于定义类型;
- ?提供灵活性;
- extends和super通过PECS原则控制数据流向。
下次遇到泛型时,你会自信地说:So easy!
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...