关于通配符的一些小问题

首先我们知道java有两种通配符

<? extend class>上界通配符:表示存储的都是class的子类(包括本身)

<? super class>下界通配符:表示存储的是class的父类(及其本身)

这两个通配符都各自有一个特点

  1. 上界通配符只能取,不能存
  2. 下界通配符只能存,不能取

下面举一个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为祖先的子树有很多,不同子树并不兼容,由于实参可能来自于任何一颗子树,所以你的插入很可能破坏函数实参。