通用容器类定义
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericContainer<T> {
private List<T> dataList;
public GenericContainer() {
dataList = new ArrayList<>();
}
// 添加数据
public void add(T item) {
dataList.add(item);
}
// 获取数据
public T get(int index) {
if (index >= 0 && index < dataList.size()) {
return dataList.get(index);
}
return null;
}
// 遍历数据
public void traverse() {
Iterator<T> iterator = dataList.iterator();
while (iterator.hasNext()) {
T item = iterator.next();
System.out.println(item);
}
}
}
类型擦除问题处理
- 问题:Java 泛型是在编译期实现的,在运行时,泛型类型信息会被擦除,所有泛型类型参数都用其限定类型(无限定的类型参数用
Object
)代替。这可能导致在运行时无法获取实际的类型信息,从而引发潜在的类型安全问题。
- 解决方法:虽然运行时无法获取确切的泛型类型信息,但在编译期编译器会利用泛型类型参数进行类型检查。比如在
GenericContainer
类中,编译器会确保添加到容器中的数据类型与声明的泛型类型一致,避免了在运行时出现 ClassCastException
。此外,可以通过 instanceof
结合反射来进行一定程度的类型检查,但要注意反射也有局限性且会增加代码复杂性。
通配符增强泛型灵活性和安全性
- 无界通配符
<?>
:
- 示例:
List<?>
表示未知类型的 List
。
- 用途:用于当你只需要对集合进行读操作,而不需要关心集合中元素的具体类型时。例如,
printList(List<?>)
方法可以接受任何类型的 List
并打印其元素,因为 Object
类型足以处理所有可能的元素类型。
- 上界通配符
<? extends Type>
:
- 示例:
List<? extends Number>
表示元素类型是 Number
或其子类型的 List
。
- 用途:常用于读取数据。例如,一个方法接受
List<? extends Number>
作为参数,可以安全地读取 Number
类型的值,因为所有 Number
的子类型(如 Integer
、Double
等)都可以赋值给 Number
。
- 下界通配符
<? super Type>
:
- 示例:
List<? super Integer>
表示元素类型是 Integer
或其父类型(如 Number
、Object
)的 List
。
- 用途:常用于写入数据。例如,一个方法接受
List<? super Integer>
作为参数,可以安全地向列表中添加 Integer
类型的元素,因为所有 Integer
的父类型都能接受 Integer
类型的值。
受限通配符的必要性及应用场景
- 必要性:
- 增强类型安全性:通过限定泛型类型的范围,确保在编译期捕获更多类型错误,避免运行时的
ClassCastException
。
- 提高代码灵活性:在保证类型安全的前提下,允许代码处理不同类型但有一定继承关系的对象,减少重复代码。
- 应用场景:
- 上界通配符场景:如编写一个计算
List<? extends Number>
中所有数字之和的方法,方法不需要关心具体是 Integer
还是 Double
等,只需要知道它们都是 Number
的子类型,从而可以安全地调用 Number
的方法进行计算。
- 下界通配符场景:假设要向一个列表中添加
Integer
类型的元素,而这个列表可能是 List<Integer>
、List<Number>
甚至 List<Object>
,使用 List<? super Integer>
作为参数类型可以确保能正确添加 Integer
元素,同时也增加了方法的通用性。