第四章:集合框架
4.4 泛型
1. 泛型概述
泛型(Generics)是Java SE 5.0引入的一项重要特性,它允许在定义类、接口或方法时使用类型参数(Type Parameters)。泛型的主要目的是提供编译时的类型安全检查,并消除强制类型转换的需要。
泛型的主要优点:
- 类型安全:编译时检查类型,避免运行时类型转换错误。
- 代码复用:可以编写更通用的代码,减少重复。
- 消除强制类型转换:使代码更简洁、可读性更高。
2. 泛型的基本语法
泛型通过尖括号<>定义类型参数,可以用于类、接口和方法。
2.1 泛型类
public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
T是类型参数,可以是任何非基本数据类型(如String、Integer等)。- 使用时指定具体类型:
Box<String> stringBox = new Box<>(); stringBox.setContent("Hello, Generics!");
2.2 泛型接口
public interface List<T> {
void add(T element);
T get(int index);
}
- 实现泛型接口时需指定具体类型:
public class StringList implements List<String> { // 实现方法 }
2.3 泛型方法
public <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
- 调用时无需显式指定类型(编译器自动推断):
Integer[] intArray = {1, 2, 3}; printArray(intArray);
3. 泛型通配符
泛型通配符?用于表示未知类型,通常用于方法参数或变量声明。
3.1 无界通配符
public void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
- 可以接受任何类型的
List,但只能读取(不能写入)。
3.2 上界通配符(? extends T)
public double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
- 表示类型是
Number或其子类(如Integer、Double)。
3.3 下界通配符(? super T)
public void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
- 表示类型是
Integer或其父类(如Number、Object)。
4. 泛型擦除
Java的泛型是通过类型擦除(Type Erasure)实现的,即在编译时擦除类型信息,运行时只保留原始类型(Raw Type)。
4.1 擦除规则
- 泛型类或方法中的类型参数会被替换为
Object(无界类型)或上界类型。 - 例如:编译后等同于:
List<String> list = new ArrayList<>();List list = new ArrayList(); // 原始类型
4.2 泛型与数组的限制
- 不能创建泛型数组:
// 编译错误! List<String>[] array = new List<String>[10]; - 但可以通过通配符实现:
List<?>[] array = new List<?>[10];
5. 泛型的实际应用
5.1 集合框架中的泛型
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// names.add(123); // 编译错误!
5.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; }
}
6. 常见问题与注意事项
- 基本类型不能作为泛型参数:需使用包装类(如
Integer代替int)。 - 泛型不能用于静态上下文的类型参数:
public class Box<T> { // 错误!静态方法不能使用类的类型参数 public static T staticMethod() { ... } } - 避免过度使用通配符:可能降低代码可读性。
7. 总结
泛型是Java集合框架的核心特性之一,它通过类型参数化提高了代码的安全性和复用性。掌握泛型的使用和通配符规则,能够更高效地编写健壮的Java程序。
练习:
- 定义一个泛型方法,用于交换数组中两个元素的位置。
- 创建一个泛型类
Cache,支持存储任意类型的值,并提供获取和设置方法。
