泛型与集合
泛型(Generics)是 Java 5 引入的特性,让集合可以指定元素类型。
泛型的作用
| 作用 | 说明 |
|---|---|
| 类型安全 | 编译期检查类型,避免运行时错误 |
| 消除转型 | 无需手动类型转换 |
| 代码清晰 | 明确集合元素类型 |
无泛型 vs 有泛型
无泛型(Java 5 之前)
Java
List list = new ArrayList();
list.add("Hello");
list.add(100); // 编译通过,运行时可能出错
String s = (String) list.get(0); // 需手动转型
Integer i = (Integer) list.get(1); // 可能 ClassCastException
有泛型
Java
List<String> list = new ArrayList<>();
list.add("Hello");
list.add(100); // 编译错误,类型不匹配
String s = list.get(0); // 无需转型
泛型集合基本用法
Java
// List 泛型
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// Set 泛型
Set<String> names = new HashSet<>();
// Map 泛型(键和值都指定类型)
Map<String, Integer> scores = new HashMap<>();
scores.put("张三", 90);
int score = scores.get("张三"); // 无需转型
// 多层嵌套泛型
Map<String, List<Integer>> map = new HashMap<>();
泛型方法
Java
// 泛型方法
public static <T> T getFirst(List<T> list) {
return list.isEmpty() ? null : list.get(0);
}
// 使用
String first = getFirst(Arrays.asList("A", "B"));
Integer firstNum = getFirst(Arrays.asList(1, 2, 3));
类型擦除
Java 泛型通过类型擦除实现:
Java
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// 运行时两者都是 ArrayList,泛型信息被擦除
System.out.println(strings.getClass() == integers.getClass()); // true
类型擦除后果:
- 运行时无法获取泛型具体类型
- 不能创建泛型数组:
new List<String>[10](非法) - 不能创建泛型实例:
new T()(非法)
泛型通配符
无界通配符
Java
// 可以接收任何类型的 List
public void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
限制:List 只能读取(作为 Object),不能添加(除 null)。
上界通配符 <? extends T>
Java
// 可以接收 T 或 T 的子类
public double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue();
}
return total;
}
// 使用
sum(Arrays.asList(1, 2, 3)); // List<Integer>
sum(Arrays.asList(1.0, 2.0, 3.0)); // List<Double>
限制:List<? extends Number> 只能读取,不能添加。
下界通配符 <? super T>
Java
// 可以接收 T 或 T 的父类
public void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3); // 可以添加 Integer
}
// 使用
List<Number> numbers = new ArrayList<>();
addNumbers(numbers); // List<Number>
List<Object> objects = new ArrayList<>();
addNumbers(objects); // List<Object>
特点:List<? super Integer> 可以添加 Integer,读取只能作为 Object。
PECS 原则
Producer-Extends, Consumer-Super:
- Producer(生产者/读取):用
? extends T - Consumer(消费者/写入):用
? super T
Java
// 从集合读取 → extends
public void read(List<? extends Number> source) {
Number n = source.get(0); // 读取
}
// 向集合写入 → super
public void write(List<? super Integer> target) {
target.add(1); // 写入
}
// 复制:src读取(dst),dst写入(src)
public void copy(List<? extends Number> src, List<? super Number> dst) {
for (Number n : src) {
dst.add(n);
}
}
要点总结
- 泛型保证类型安全,消除手动转型
- List、Map<K, V> 指定元素类型
- Java 泛型通过类型擦除实现,运行时无泛型信息
- PECS:生产者用 extends,消费者用 super
📝 发现内容有误?点击此处直接编辑