在Java中,泛型通配符允许我们在定义方法参数或变量时,接受不同类型的泛型实例。了解通配符的上限和下限,以及野蛮类型,有助于编写更具通用性的代码。以下是一些关键概念、常见问题及其解决方案,以及代码示例。
- 通配符上限(Upper Bounds)
- 概念:使用
? extends T
表示类型是T
或其子类型。T
被称为上限类型。 - 示例:
- 概念:使用
void addToList(List<? extends Number> numbers) {
numbers.add(10); // 错误,不能添加元素
Number num = numbers.get(0); // 正确,可以安全地读取
}
- 问题:只能读取,不能添加元素。
- 避免:使用通配符上限时,明确代码的读写需求。
- 通配符下限(Lower Bounds)
- 概念:使用
? super T
表示类型是T
或其父类型。T
被称为下限类型。 - 示例:
- 概念:使用
void copyNumbers(List<? super Number> dest, List<Number> src) {
dest.addAll(src); // 正确,可以添加元素
Number num = dest.get(0); // 错误,编译错误,无法安全地读取
}
- 问题:只能添加元素,不能安全地读取元素。
- 避免:使用通配符下限时,确保代码主要关注写入操作。
- 野蛮类型(Raw Types)
- 概念:不使用泛型或者使用泛型但不指定类型参数,如
List
而不是List<String>
。 - 示例:
- 概念:不使用泛型或者使用泛型但不指定类型参数,如
List list = new ArrayList(); // 野蛮类型
list.add("String"); // 可以添加任何类型
Object obj = list.get(0); // 获取的是Object类型,需要显式转换
- 问题:类型安全检查缺失,可能导致运行时ClassCastException。
- 避免:尽可能避免使用野蛮类型,除非与遗留的非泛型代码交互。
- 通配符的限制
- 问题:不能创建泛型通配符类型的实例,如
List<? extends Number> numbers = new ArrayList<>()
是不允许的。 - 避免:在需要创建实例时,指定确切的类型,如
List<Number> numbers = new ArrayList<Number>()
。
- 问题:不能创建泛型通配符类型的实例,如
- 边界冲突
- 问题:不能同时指定上下限,如
? extends Number & Comparable<?>
是非法的。 - 避免:若需同时限制上下边界,可能需要使用自定义接口或类作为边界。
- 问题:不能同时指定上下限,如
理解并熟练使用泛型通配符的上限、下限和野蛮类型,可以编写出更灵活且类型安全的代码。然而,过度使用通配符可能导致代码可读性降低,因此在设计API时要权衡通用性和清晰度。