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, 但后续可能抛出 NullPointerException
5.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; // 编译错误!