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




