
Java 泛型详解
Java 泛型(Generics)是 Java 5 引入的一个重要特性,它允许在类、接口和方法中使用类型参数。泛型的主要目的是提高代码的类型安全性和复用性,同时减少强制类型转换的需求。
一、什么是泛型?
泛型允许你在定义类、接口或方法时使用类型参数,而不是具体的类型。这些类型参数在实际使用时会被替换为具体的类型(如 String、Integer 等)。泛型的核心思想是“参数化类型”,即让代码能够适用于多种类型,而不需要为每种类型重复编写代码。
二、泛型的好处
类型安全性:
- 使用泛型可以避免将错误类型的对象添加到集合中。
- 编译器会在编译期检查类型是否匹配,从而避免运行时的
ClassCastException。
代码复用:
- 泛型让你可以编写通用的代码,适用于多种类型,而不需要为每种类型单独实现。
减少类型转换:
- 在没有泛型的情况下,从集合中取出元素时需要显式地进行类型转换。使用泛型后,编译器会自动推断类型,无需手动转换。
更清晰的代码:
- 泛型使代码更加直观,调用者可以清楚地知道某个类或方法支持哪些类型。
三、泛型的基本语法
1. 定义泛型类
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}<T>是类型参数,表示这个类可以处理任意类型的对象。T是一个占位符,代表某种类型,具体类型在实例化时确定。
使用泛型类:
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String value = stringBox.getItem(); // 不需要类型转换
Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);
int number = integerBox.getItem();2. 定义泛型方法
泛型方法可以在普通类中定义,也可以在泛型类中定义。
public class Utils {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}使用泛型方法:
String[] strings = {"A", "B", "C"};
Utils.printArray(strings);
Integer[] numbers = {1, 2, 3};
Utils.printArray(numbers);3. 定义泛型接口
public interface Container<T> {
void add(T item);
T get(int index);
}使用泛型接口:
public class StringContainer implements Container<String> {
private List<String> items = new ArrayList<>();
@Override
public void add(String item) {
items.add(item);
}
@Override
public String get(int index) {
return items.get(index);
}
}四、泛型的通配符
1. 无界通配符:<?>
表示未知类型,可以匹配任何类型。
public void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}使用场景:当方法只需要读取集合中的元素,而不需要修改集合时,可以使用 <?>。
2. 上界通配符:<? extends T>
表示类型参数必须是 T 或其子类。
public void processNumbers(List<? extends Number> numbers) {
for (Number num : numbers) {
System.out.println(num.doubleValue());
}
}使用场景:当你希望方法接受某种类型及其子类的集合时,可以使用 <? extends T>。
3. 下界通配符:<? super T>
表示类型参数必须是 T 或其父类。
public void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}使用场景:当你希望方法能够向集合中添加某种类型及其子类的对象时,可以使用 <? super T>。
五、泛型的限制
不能使用基本类型:
- 泛型不支持基本类型(如
int、double等),只能使用包装类(如Integer、Double等)。 - 例如:
Box<int>是非法的,但Box<Integer>是合法的。
- 泛型不支持基本类型(如
类型擦除:
- 泛型信息在编译后会被擦除(Type Erasure),这意味着运行时无法获取泛型的具体类型。
- 例如:
new ArrayList<String>()和new ArrayList<Integer>()在运行时都是ArrayList类型。
不能创建泛型数组:
- 你不能直接创建泛型数组,例如
new T[10]是非法的。 - 可以通过其他方式绕过此限制,例如使用
Object数组并进行类型转换。
- 你不能直接创建泛型数组,例如
