<? extends ClassType>、<? super ClassType>中get add方法被限制的原因
问号是通配符,用在声明引用类型的尖括号里,目的解决“苹果的盘子”不是“水果的盘子”之类的问题。
class Fruit {}
class Apple extends Fruit {}
class Plate<T>{
private T item;
public Plate(){}
public void set(T t){item=t;}
public T get(){return item;}
}
定义一个水果盘子,然后直接用苹果盘子去赋值是不行的,也就是说苹果盘子不是水果盘子。
Plate<Fruit> p=new Plate<Apple>();
//error: incompatible types: Plate<Apple> cannot be converted to Plate<Fruit>
这个时候就要用问号通配符告诉编译器,我现在声明的是一个水果及其基类的盘子,也就是这样
Plate<? extends Fruit> p=new Plate<Apple>();
这样的操作会带来下面两个问题:
- <? extends ClassType> 不能进行set()或List中的add()操作,也就是不能执行参数列表中含有T的方法。
- <? super ClassType>执行get()操作会返回一个Object,也就是返回值为T的方法会返回一个Object。
我们知道在下面两种情况时,ClassTypeB必须和ClassTypeA是相同类型,或者是其派生类。
ClassTypeD 必须和ClassTypeC 是相同类型,或者是其派生类。
public void add(ClassTypeA t){
}
add(new ClassTypeB());
public ClassTypeC get(){
return new ClassTypeC();
}
ClassTypeD d=get();
同样的,在调用泛型类中的方法时也必须遵守这两条规则,不然就会造成信息丢失。
假设下列情况
Plate<? extends Fruit> p=new Plate<【1】>();
这种情下位置【1】可以填写Fruit、Apple、GreenApple三种类。
现在我们现在将【1】的位置填入Fruit的派生类GreenApple.
Plate<? extends Fruit> p=new Plate<GreenApple>();
我们执行p.set(【2】);我们理所当然的认为【2】应该填new GreenApple()而不是Apple或者Fruit,但是编译器只保证p这个盘子的实际类型必须为Fruit或其派生类,但不知道到底是Fruit、Apple、GreenApple中的哪一个。也就是说编译器无法判断你会不会进行下列操作
p.set(new Apple);即GreenApple item=new Apple();基类赋值给子类
所以说,编译器就直接把<? extends Fruit>调用参数列表中含有T的普通方法的权限给禁掉了。
接下来看
Plate<? super Apple> p=new Plate<【1】>();
这种情下位置【1】可以填写Food、Fruit、Apple三种类。
现在我们现在将【1】的位置填入Apple的基类Food.
Plate<? super Apple> p=new Plate<Food>();
假设我们执行【2】 food=p.get();同样编译器也不知道盘子p的类型究竟是Apple、Fruit还是Food.也就是说如果返回一个确切无法避免下列情况
Fruit fruit=p.get();即Fruit fruit=new Food();基类赋值给子类
但是还有一个解决方案,那就是返回一个Object,因为Object是所有类的基类。所以说<? super ClassType>的get方法会返回一个Object。
如果有错误请帮忙指出。Thanks♪(・ω・)ノ