extends通配符
约 879 字大约 3 分钟
2025-07-17
在泛型中,Pair<Integer> 不是 Pair<Number> 的子类。这意味着,即使 Integer 是 Number 的子类,Pair<Integer> 也不能直接作为 Pair<Number> 使用。
假设有如下 Pair<T> 的定义:
public class Pair<T> { ... }以及一个接收 Pair<Number> 类型参数的静态方法:
public class PairHelper {
static int add(Pair<Number> p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
}直接传入 Pair<Number> 类型的实例是可以正常工作的:
int sum = PairHelper.add(new Pair<Number>(1, 2));但是,如果尝试传入 Pair<Integer> 类型的实例:
Pair<Integer> p = new Pair<>(123, 456);
int n = add(p); // 编译错误会发生编译错误,因为 Pair<Integer> 不能被转换为 Pair<Number>。
5.1 extends 通配符的使用
为了使方法能够接受 Pair<Integer> 类型的参数,可以使用 extends 通配符,将方法参数类型修改为 Pair<? extends Number>:
static int add(Pair<? extends Number> p) { ... }这样,该方法就可以接收所有泛型类型为 Number 或 Number 子类的 Pair 类型。这种使用 <? extends Number> 的泛型定义称之为上界通配符(Upper Bounds Wildcards),它把泛型类型 T 的上界限定在 Number。
例如,可以传入 Pair<Integer>、Pair<Double>、Pair<BigDecimal> 等类型。
5.2 extends 通配符的限制
如果对 Pair<? extends Number> 类型调用 getFirst() 方法,实际的方法签名变成了:
<? extends Number> getFirst();返回值是 Number 或 Number 的子类,可以安全地赋值给 Number 类型的变量:
Number x = p.getFirst();但是,不能确定实际类型就是 Integer,因此,下面的代码无法通过编译:
Integer x = p.getFirst(); // 编译错误5.3 set 方法的限制
考虑 Pair<T> 的 set 方法:
public void setFirst(T first) {
this.first = first;
}对于 Pair<? extends Number> 类型的实例,不能调用 setFirst() 方法并传入任何 Number 的子类型,例如:
Pair<Integer> p = new Pair<>(123, 456);
add(p);
static int add(Pair<? extends Number> p) {
p.setFirst(new Integer(123)); // 编译错误
return 0;
}原因是,如果传入的 p 是 Pair<Double>,那么 setFirst() 显然无法接受 Integer 类型。
唯一的例外是可以给方法参数传入 null:
p.setFirst(null); // ok, 但后续可能抛出 NullPointerException5.4 extends 通配符的作用
考察 Java 标准库的 java.util.List<T> 接口。定义一个方法来处理列表的每个元素:
int sumOfList(List<? extends Integer> list) {
int sum = 0;
for (int i = 0; i < list.size(); i++) {
Integer n = list.get(i);
sum = sum + n;
}
return sum;
}使用 List<? extends Integer> 而不是 List<Integer> 的原因在于,List<? extends Integer> 具有以下限制:
- 允许调用
get()方法获取Integer的引用; - 不允许调用
set(? extends Integer)方法并传入任何Integer的引用(null除外):由于编译器不知道实际的类型(它是Integer的某个子类型或其本身,但具体是 哪个 类型未知),所以它无法验证你尝试放入list中的对象是否与list的实际类型兼容。
因此,方法参数类型 List<? extends Integer> 表明该方法内部只会读取 List 的元素,不会修改 List 的元素。换句话说,这是一个对参数 List<? extends Integer> 进行只读的方法。
5.5 使用 extends 限定 T 类型
在定义泛型类型 Pair<T> 的时候,也可以使用 extends 通配符来限定 T 的类型:
public class Pair<T extends Number> { ... }现在,只能定义 Number 或 Number 子类的 Pair:
Pair<Number> p1 = null;
Pair<Integer> p2 = new Pair<>(1, 2);
Pair<Double> p3 = null;非 Number 类型将无法通过编译:
Pair<String> p1 = null; // 编译错误!