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
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除