Java 超类型通配符,还有一种通配符,与形式<? extends E>
正好相反,它的形式为<? super E>
,称为超类型通配符,表示E的某个父类型。它有什么用呢?有了它,我们就可以更灵活地写入了。
如果没有这种语法,写入会有一些限制。来看个例子,我们给DynamicArray添加一个方法:
public void copyTo(DynamicArray<E> dest){
for(int i=0; i<size; i++){
dest.add(get(i));
}
}
这个方法也很简单,将当前容器中的元素添加到传入的目标容器中。我们可能希望这么使用:
DynamicArray<Integer> ints = new DynamicArray<Integer>();
ints.add(100);
ints.add(34);
DynamicArray<Number> numbers = new DynamicArray<Number>();
ints.copyTo(numbers);
Integer是Number的子类,将Integer对象拷贝入Number容器,这种用法应该是合情合理的,但Java会提示编译错误,理由我们之前也说过了,期望的参数类型是Dynamic-Array<Integer>
, DynamicArray<Number>
并不适用。
如之前所说,一般而言,不能将DynamicArray<Integer>
看作DynamicArray<Number>
,但我们这里的用法是没有问题的,Java解决这个问题的方法就是超类型通配符,可以将copyTo代码改为:
public void copyTo(DynamicArray<? super E> dest){
for(int i=0; i<size; i++){
dest.add(get(i));
}
}
这样,就没有问题了。
超类型通配符另一个常用的场合是Comparable/Comparator
接口。同样,我们先来看下如果不使用会有什么限制。以前面计算最大值的方法为例,它的方法声明是:
public static <T extends Comparable<T>> T max(DynamicArray<T> arr)
这个声明有什么限制呢?举个简单的例子,有两个类Base和Child, Base的代码是:
class Base implements Comparable<Base>{
private int sortOrder;
public Base(int sortOrder) {
this.sortOrder = sortOrder;
}
@Override
public int compareTo(Base o) {
if(sortOrder < o.sortOrder){
return -1;
}else if(sortOrder > o.sortOrder){
return 1;
}else{
return 0;
}
}
}
Base代码很简单,实现了Comparable接口,根据实例变量sortOrder进行比较。Child代码是:
class Child extends Base {
public Child(int sortOrder) {
super(sortOrder);
}
}
这里,Child非常简单,只是继承了Base。注意:Child没有重新实现Comparable接口,因为Child的比较规则和Base是一样的。我们可能希望使用前面的max方法操作Child容器,如下所示:
DynamicArray<Child> childs = new DynamicArray<Child>();
childs.add(new Child(20));
childs.add(new Child(80));
Child maxChild = max(childs);
遗憾的是,Java会提示编译错误,类型不匹配。为什么不匹配呢?我们可能会认为,Java会将max方法的类型参数T推断为Child类型,但类型T的要求是extends Comparable<T>
,而Child并没有实现Comparable<Child>
,它实现的是Comparable<Base>
。
但我们的需求是合理的,Base类的代码已经有了关于比较所需要的全部数据,它应该可以用于比较Child对象。解决这个问题的方法,就是修改max的方法声明,使用超类型通配符,如下所示:
public static <T extends Comparable<? super T>> T max(DynamicArray<T> arr)
这么修改一下就可以了,这种写法比较抽象,将T替换为Child,就是:
Child extends Comparable<? super Child>
<? super Child>
可以匹配Base,所以整体就是匹配的。
我们比较一下类型参数限定与超类型通配符,类型参数限定只有extends形式,没有super形式,比如,前面的copyTo方法的通配符形式的声明为:
public void copyTo(DynamicArray<? super E> dest)
如果类型参数限定支持super形式,则应该是:
public <T super E> void copyTo(DynamicArray<T> dest)
事实是,Java并不支持这种语法。
前面我们说过,对于有限定的通配符形式<? extends E>
,可以用类型参数限定替代,但是对于类似上面的超类型通配符,则无法用类型参数替代。
通配符比较
三种通配符形式<? >
、<? super E>
和<? extends E>
,它们比较容易混淆,我们总结比较如下:
1)它们的目的都是为了使方法接口更为灵活,可以接受更为广泛的类型。
2)<? super E>
用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象,它不能被类型参数形式替代。
3)<? >
和<? extends E>
用于灵活读取,使得方法可以读取E或E的任意子类型的容器对象,它们可以用类型参数的形式替代,但通配符形式更为简洁。
Java容器类的实现中,有很多使用通配符的例子,比如,类Collections中就有如下方法:
public static <T extends Comparable<? super T>> void sort(List<T> list)
public static <T> void sort(List<T> list, Comparator<? super T> c)
public static <T> void copy(List<? super T> dest, List<? extends T> src)
public static <T> T max(Collection<? extends T> coll,
Comparator<? super T> comp)
酷客教程相关文章:
评论前必须登录!
注册