extends通配符转载
原创我们已经讨论过泛型继承关系: Pair<Integer> 不是 Pair<Number> 的子类。
假设我们定义 Pair<T> :
public class Pair { ... }
然后,我们瞄准 Pair<Number> Type编写接收参数类型的静态方法。 Pair<Number> :
public class PairHelper {
static int add(Pair p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
}
上述代码可以正常编译。使用时,我们传入:
int sum = PairHelper.add(new Pair(1, 2));
注意:传入的类型为 Pair<Number> ,实际参数类型为 (Integer, Integer) 。
由于实际参数是 Integer 类型,请尝试传入 Pair<Integer> :
public class Main {
}
class Pair
Run
直接运行时会出现编译错误:
incompatible types: Pair cannot be converted to Pair
原因是显而易见的,因为 Pair<Integer> 不是 Pair<Number> 因此, add(Pair<Number>) 不接受参数类型 Pair<Integer> 。
但是从 add() 该方法的代码知道传入的 Pair<Integer> 是完全符合内部代码的类型规范,因为语句:
Number first = p.getFirst();
Number last = p.getLast();
实际类型为 Integer ,引用类型为 Number ,没问题。问题是方法参数类型是固定的,只能传入。 Pair<Number> 。
没有办法让方法参数接受。 Pair<Integer> ?有一种方法,这就是使用。 Pair<? extends Number> 使该方法接收所有泛型类型 Number 或 Number 子类的 Pair 类型。我们按如下方式重写代码:
public class Main {
}
class Pair
Run
通过这种方式,该方法被传入。 Pair<Integer> 类型,则它符合参数。 Pair<? extends Number> 类型。此用途 <? extends Number> 的一般定义称为上界通配符(Upper Bounds Wildcards),即泛型类型。 T 的上界 Number 了。
除了能够通过 Pair<Integer> 类型,我们也可以传入 Pair<Double> 类型, Pair<BigDecimal> 类型等,因为 Double 和 BigDecimal 都是 Number 的子类。
如果我们检查一下对 Pair<? extends Number> 类型调用 getFirst() 方法,则实际的方法签名将变为:
extends Number> getFirst();
也就是说,返回值为 Number 或 Number 因此,可以安全赋值给 Number 类型的变量:
Number x = p.getFirst();
那么,我们就无法预测实际的类型是 Integer 例如,无法编译以下代码:
Integer x = p.getFirst();
这是因为实际返回类型可能是 Integer ,也可能是 Double 或其他类型,则编译器只能确定该类型必须是 Number 子类(包括 Number 类型本身),但无法确定具体类型。
让我们再看一遍。 Pair<T> 的 set 方法:
public class Main {
}
class Pair
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
public void setFirst(T first) {
this.first = first;
}
public void setLast(T last) {
this.last = last;
}
}
Run
毫不奇怪,我们得到一个编译错误:
incompatible types: Integer cannot be converted to CAP#1
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
编译错误发生在 p.setFirst() 传入的参数包括 Integer 类型。有些童鞋会问,因为 p 的定义是 Pair<? extends Number> ,那么 setFirst(? extends Number) 为什么我不能进去? Integer ?
原因也是擦拭的方法。如果我们进去了 p 是 Pair<Double> ,显然它满足参数定义。 Pair<? extends Number> ,然而, Pair<Double> 的 setFirst() 显然是不可接受的 Integer 类型。
这就是 <? extends Number> 通配符的一个重要限制是:方法参数签名。 setFirst(? extends Number) 没有交付任何 Number 类型给 setFirst(? extends Number) 。
这里唯一的例外是您可以传入方法参数。 null :
p.setFirst(null); // ok, 但它将在稍后被抛出。NullPointerException
p.getFirst().intValue(); // NullPointerException
extends通配符的作用
如果我们检查Java标准库的 java.util.List<T> 接口,该接口实现类似于“变量数组”的列表,其主要功能包括:
public interface List {
int size(); // 获取个数
T get(int index); // 根据索引获取指定的元素。
void add(T t); // 添加新元素
void remove(T t); // 删除现有元素
}
现在,让我们定义一个方法来处理列表的每个元素:
int sumOfList(List extends Integer> list) {
int sum = 0;
for (int i=0; i
为什么我们定义的方法参数类型是 List<? extends Integer> 而不是 List<Integer> ?从该方法的内部代码中,传入 List<? extends Integer> 或者 List<Integer> 是完全一样的,然而,注意到 List<? extends Integer> 的限制:
- 允许调用
get()方法获取Integer的引用; - 不允许呼叫
set(? extends Integer)方法,并在任何Integer的引用(null除外)。
因此,方法参数类型 List<? extends Integer> 指示该方法内部仅读取 List 元素,不会修改 List 元素(因为它无法调用) add(? extends Integer) 、 remove(? extends Integer) 这些方法。换句话说,这是一对参数。 List<? extends Integer> 创建只读方法(恶意调用)。 set(null) 除外)。
使用extends限定T类型
在定义泛型类型时 Pair<T> 也可以使用。 extends 使用通配符进行限定 T 的类型:
public class Pair { ... }
现在,我们只能定义:
Pair p1 = null;
Pair p2 = new Pair<>(1, 2);
Pair p3 = null;
因为 Number 、 Integer 和 Double 都符合 <T extends Number> 。
非 Number 将不编译类型:
Pair p1 = null; // compile error!
Pair
因为 String 、 Object 都不符合 <T extends Number> 因为他们不是 Number 类型或 Number 的子类。
小结
使用类似 <? extends Number> 当通配符是方法参数时,表示:
-
方法可以在内部调用以获取。
Number引用的方法,例如:Number n = obj.getFirst();; -
方法内部无法调用传入
Number引用的方法(null例外),例如:obj.setFirst(Number n);。
也就是说,用一句话概括:使用 extends 通配符表示它可以读取,但不能写入。
使用类似 <T extends Number> 在定义泛型类时,它意味着:
- 泛型类型是有限的
Number以及Number的子类。 - 地址: https://www.liaoxuefeng.com/wiki/1252599548343744/1265105899616928
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123




