在Java泛型中,extends
通配符用于限定泛型类型的上界,即指定泛型可以是某个类型或其子类型。它有两种常见用法:类型参数限定和通配符限定,下面详细介绍:
1. 类型参数限定(在类/方法定义中)
在定义泛型类或方法时,使用extends
关键字限制泛型类型必须是某个类(或接口)的子类。
语法:<T extends 上限类型>
- 示例1:限定泛型为
Number
的子类// 泛型类:T必须是Number或其子类(如Integer、Double) class GenericClass<T extends Number> {private T value;public GenericClass(T value) {this.value = value;}public double getDoubleValue() {return value.doubleValue(); // 可调用Number的方法} }// 使用: GenericClass<Integer> intClass = new GenericClass<>(10); // 合法 GenericClass<String> strClass = new GenericClass<>("abc"); // 编译错误(String不是Number子类)
- 示例2:限定多个接口(用
&
分隔)// T必须同时实现Comparable和Serializable接口 class MultiInterface<T extends Comparable<T> & Serializable> {// ... }
2. 通配符限定(在方法参数或变量声明中)
使用? extends 上限类型
表示未知类型,但必须是指定类型的子类,常用于读取数据(因为写入时类型不确定)。
语法:? extends 上限类型
- 示例1:方法参数中使用通配符
// 只能处理Number或其子类的容器,且只能读取数据 public static void printNumbers(Collection<? extends Number> nums) {for (Number num : nums) {System.out.println(num);}// nums.add(10); // 编译错误:无法向通配符类型添加元素 }// 使用: List<Integer> intList = Arrays.asList(1, 2, 3); printNumbers(intList); // 合法 List<String> strList = Arrays.asList("a", "b"); printNumbers(strList); // 编译错误(String不是Number子类)
- 示例2:通配符与泛型类结合
class Box<T> {private T value;// ... getter/setter }// 定义一个方法,接收"上限为Number"的Box,只能读取 public static void processBox(Box<? extends Number> box) {Number num = box.getValue(); // 合法// box.setValue(10); // 编译错误:无法确定具体类型 }
3. extends通配符的核心规则
- 只读原则:
? extends T
类型的集合只能读取元素(返回类型为T
或其父类),不能写入元素(因为无法确定具体类型)。
例如:List<? extends Number>
可以读取Number
,但无法添加任何元素(包括Number
本身)。 - 类型传递性:如果
A
是B
的子类,List<A>
是List<? extends B>
的子类型。
4. 与super通配符的对比
extends
通配符(? extends T
):限定类型上限(必须是T或其子类),用于读取。super
通配符(? super T
):限定类型下限(必须是T或其父类),用于写入。
示例:// 写入场景用super: public static void addNumber(List<? super Integer> list, Integer num) {list.add(num); // 合法,因为list元素类型至少是Integer的父类 }
总结
extends
通配符是Java泛型中实现类型安全和灵活兼容的重要工具:
- 定义泛型时,用
<T extends 上限>
限制类型范围,确保类型操作的合法性。 - 使用泛型时,用
? extends 上限
允许接收子类类型,同时保证读取操作的安全(但牺牲写入能力)。
合理使用通配符可以避免类型转换异常,提升代码的复用性和可维护性。