关于通配符的一些小问题
首先我们知道java有两种通配符
<? extend class>上界通配符:表示存储的都是class的子类(包括本身)
<? super class>下界通配符:表示存储的是class的父类(及其本身)
这两个通配符都各自有一个特点
- 上界通配符只能取,不能存
- 下界通配符只能存,不能取
下面举一个super的例子
//这是类的继承关系 class A { } class B extends A { } class C extends B { }
我们可以定义
List<? super B> list = new ArrayList<B>();
又或者是因为super关系,可以这么定义
List<? super B> list = new ArrayList<A>();
但是如果我们在进行add的操作的时候,定义如下函数
static void f(List<? super B> list) {
//list.add(new A()); //编译失败
list.add(new B()); //编译成功
list.add(new C()); //编译成功
}
就会发生很奇怪的问题,明明是super关系,在add的时候却只能add B的子类或其本身,而不能add B的父类,这跟前面的理论有所矛盾。
发生这样的问题在于,list在存储的时候会将C向上转型为B,也就是 list.add((B)new C());
,而向上转型是隐式的,所以add 其子类也就是add其本身,所以是可以的。
接下来再来说说为什么不能add其父类A,原因在于?可以匹配所有B 的父类,这个具体的类型在编译期间是无法预知的,只有程序运行起来才能具体知道类型,而编译器认为手动add的内容必须是“安全”的,所以就不可以添加其父类。
关于super只能存,而不能读的解释
因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是B的基类,那往里存粒度比B小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。注意一点,虽然只能用object来装,但是对象的信息并没有丢失!!!。
关于extend只能读,而不能写的解释
由于指定了B为所有元素的“根”,你任何时候都可以安全的用B来使用容器里的元素,但是插入有问题,由于供奉B为祖先的子树有很多,不同子树并不兼容,由于实参可能来自于任何一颗子树,所以你的插入很可能破坏函数实参。