实验二和实验三均需要进行泛型编程,这就不得不提到关于类型擦除和通配符的使用。
目录
实验中的问题
以实验三中的问题为例,在实验三中有这么一个需求,需要设计一个IntervalSet,各个方法的名称以及输入参数和spec若下图所示,具体代码如下图所示:
static <L> CommonMultiIntervalSet<L> MultiIntervalSet(IntervalSet<L> initial){
CommonMultiIntervalSet<L> map = new CommonMultiIntervalSet<>();
Set<L> tmp = new HashSet<>();
tmp = initial.labels();
Iterator<L> add = tmp.iterator();
while(add.hasNext()){
L tam = add.next();
map.insert(initial.start(tam),initial.end(tam),tam);
}
return map;
}
这里的参数我们不能直接用具体的某个类,例如Process之类的具体类,因为这个接口要被多个应用调用,所以只能用抽象的接口作为输入类型。但是在对象中具体实现的方法中采用他的List<子类>却不行,这是为什么呢?我们都知道在java中我们可以将传入值时子类传给父类。但是这里为什么不行呢?就是因为类型擦除。
类型擦除
在java对于集合类的泛型T,使用时,实际上会产生类型擦除的状况,也就是说无论向集合类传入什么参数,在运行时都会把泛型T给擦除成Object。所以说
public class Cache<T> {
T value;
public Object getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
擦除之后就是
public class Cache {
Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
所以IntervalSet<父类型>不是IntervalSet<子类型>的父类型。
那这种情况怎么办呢?我们引入了通配符?
通配符及其使用
于是就有了泛型统配符的存在,我们参数写了IntervalSet<?>,这个参数就可以接受List,这样的子类型
具体使用如下(这里只展示部分代码):
CommonMultiIntervalSet<L> MultiIntervalSet(IntervalSet<? extends L> initial){
我们注意到这里并不是直接使用?而是用了?extends XXX. 这里是指可以传入这个类和他的子类型,称为上界通配符。同样的还有?super xxx,称为下界通配符,功能与上界通配符正好相反。