super通配符转载
原创我们已经讨论过泛型继承关系: Pair<Integer>
不是 Pair<Number>
的子类。
请检查以下内容 set
方法:
void set(Pair p, Integer first, Integer last) {
p.setFirst(first);
p.setLast(last);
}
传入 Pair<Integer>
被允许,但传入的 Pair<Number>
是不允许的。
和 extends
这一次,我们想要接受,而不是通配符。 Pair<Integer>
类型,和 Pair<Number>
、 Pair<Object>
,因为 Number
和 Object
是 Integer
的父类, setFirst(Number)
和 setFirst(Object)
实际上允许接受 Integer
类型。
我们使用 super
用于重写此方法的通配符:
void set(Pair super Integer> p, Integer first, Integer last) {
p.setFirst(first);
p.setLast(last);
}
注意到 Pair<? super Integer>
表示该方法参数接受所有泛型类型。 Integer
或 Integer
父类的 Pair
类型。
可以正常编译以下代码:
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
考察 Pair<? super Integer>
的 setFirst()
方法,其方法签名实际上是:
void setFirst(? super Integer);
因此,它可以安全地传递进来。 Integer
类型。
再考察 Pair<? super Integer>
的 getFirst()
方法,其方法签名实际上是:
? super Integer getFirst();
这里需要注意的是,我们不能使用 Integer
要接收的类型 getFirst()
不会编译以下语句的返回值:
Integer x = p.getFirst();
因为如果传入的实际类型是 Pair<Number>
,则编译器不能 Number
类型转换 Integer
。
注:虽然 Number
是我们不能直接实例化的抽象类。然而,即使 Number
不是抽象类,还是不能在这里编译。此外,即将到来的 Pair<Object>
类型,则编译器不能也是 Object
类型转换 Integer
。
唯一一个可以接受 getFirst()
该方法的返回值为 Object
类型:
Object obj = p.getFirst();
因此,使用 <? super Integer>
通配符表示:
-
允许调用
set(? super Integer)
方法传入Integer
的引用; -
不允许呼叫
get()
方法获得Integer
的引用。
唯一的例外是你可以得到 Object
的引用: Object o = p.getFirst()
。
换句话说,使用 <? super Integer>
作为方法参数的通配符表示该方法的内部代码只能针对该参数写入,而不能读取。
使用super限定T类型
在定义泛型类型时 Pair<T>
也可以使用。 super
使用通配符进行限定 T
的类型:
public class Pair { ... }
现在,我们只能定义:
Pair p1 = null;
Pair p2 = new Pair<>(1, 2);
Pair
因为 Number
、 Integer
和 Object
都符合 <T super Number>
。
非 Integer
该类型及其子类将无法编译:
Pair p1 = null; // compile error!
对比extends和super通配符
让我们再复习一遍 extends
通配符。作为方法参数, <? extends T>
类型和 <? super T>
类型之间的区别在于:
-
<? extends T>
允许调用读取方法T get()
获取T
的引用,但不允许呼叫写方法set(T)
传入T
引用(传入)的null
除外); -
<? super T>
允许调用以写入方法set(T)
传入T
的引用,但不允许呼叫读方法T get()
获取T
引用的(GETObject
除外)。
一个是允许读而不允许写,另一个是允许写而不允许读。
首先,记住上面的结论,让我们来看看它。Java标准库的 Collections
类定义的 copy()
方法:
public class Collections {
// 把src复制的每个元素dest中:
public static void copy(List super T> dest, List extends T> src) {
for (int i=0; i
它的作用是将一个 List
的每个元素依次加到另一个元素上。 List
在……里面。它的第一个论点是 List<? super T>
,表示目标 List
,第二个参数 List<? extends T>
,表明 List
。我们可以简单地使用 for
用于复制的循环。在……里面 for
循环,我们可以看到对于类型 <? extends T>
的变量 src
,我们可以安全地获得类型。 T
引用的类型,而对于类型 <? super T>
的变量 dest
,我们可以安全地通过 T
的引用。
这个 copy()
该方法的定义完美地说明了 extends
和 super
的意图:
-
copy()
方法未在内部读取dest
因为它不能被调用。dest.get()
来获取T
的引用; -
copy()
该方法在内部也没有修改。src
因为它不能被调用。src.add(T)
。
这是通过编译器检查实现的。如果在方法代码中意外修改 src
,或不小心读到 dest
,将导致编译错误:
public class Collections {
// 把src复制的每个元素dest中:
public static void copy(List super T> dest, List extends T> src) {
...
T t = dest.get(0); // compile error!
src.add(t); // compile error!
}
}
这个 copy()
该方法的另一个优点是,将一个 List<Integer>
添加到 List<Number>
,但不能以相反方向添加:
// copy List to List ok:
List numList = ...;
List intList = ...;
Collections.copy(numList, intList);
// ERROR: cannot copy List to List:
Collections.copy(intList, numList);
这些都是通过 super
和 extends
通配符,并由编译器强制检查实现。
PECS原则
何时使用 extends
,何时使用 super
?为了便于记忆,我们可以使用PECS原则:Producer Extends Consumer Super。
如果你需要回来的话。 T
,它是制片人(Producer),以使用 extends
通配符;如果需要写入 T
,它是消费者(Consumer),以使用 super
通配符。
还是以 Collections
的 copy()
方法为例:
public class Collections {
public static void copy(List super T> dest, List extends T> src) {
for (int i=0; i
需要返回 T
的 src
是制片人,因此被宣布为 List<? extends T>
,需要写 T
的 dest
是消费者,因此被声明为 List<? super T>
。
无限定通配符
我们已经讨论过 <? extends T>
和 <? super T>
角色作为方法参数。事实上,Java泛型还允许使用无限通配符(Unbounded Wildcard Type),即只定义了一个。 ?
:
void sample(Pair> p) {
}
因为 <?>
通配符既没有 extends
,也没有 super
,因此:
- 不允许呼叫
set(T)
方法并传入引用(null
除外); - 不允许呼叫
T get()
方法和获取T
引用(仅获取Object
引用)。
换句话说,既不会读也不会写,那只能做一些 null
判断:
static boolean isNull(Pair> p) {
return p.getFirst() == null || p.getLast() == null;
}
在大多数情况下,可以引入泛型参数 <T>
消除 <?>
通配符:
static boolean isNull(Pair p) {
return p.getFirst() == null || p.getLast() == null;
}
<?>
通配符的一个独特功能是: Pair<?>
是所有 Pair<T>
的超类:
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
上述代码可以正常编译和运行,因为 Pair<Integer>
是 Pair<?>
可以安全地向上转换的子类。
小结
使用类似 <? super Integer>
当通配符是方法参数时,表示:
-
在该方法内部,您可以调用传入的。
Integer
引用的方法,例如:obj.setFirst(Integer n);
; -
方法无法调用Get Inside
Integer
引用的方法(Object
例外),例如:Integer n = obj.getFirst();
。
即使用 super
通配符表示您只能写而不能读。
使用 extends
和 super
后跟通配符PECS原则。
使用类似 <T super Integer>
在定义泛型类时,它意味着:
- 泛型类型是有限的
Integer
或Integer
的超类。
无限定通配符 <?>
它很少使用,而且可以使用。 <T>
趁一切还好,换掉它 <T>
类型的超类。
地址: https://www.liaoxuefeng.com/wiki/1252599548343744/1265105920586976
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除