正文
通配符
在了解通配符之前,我们首先必须要澄清一个概念,还是借用我们上面定义的Box类,假设我们添加一个这样的方法:
那么现在
Box
n
允许接受什么类型的参数?我们是否能够传入
Box
或者
Box
呢?答案是否定的,虽然Integer和Double是Number的子类,但是在泛型中
Box
或者
Box
与
Box
之间并没有任何的关系。这一点非常重要,接下来我们通过一个完整的例子来加深一下理解。
首先我们先定义几个简单的类,下面我们将用到它:
下面这个例子中,我们创建了一个泛型类
Reader
,然后在
f1()
中当我们尝试
Fruit f = fruitReader.readExact(apples);
编译器会报错,因为
List
与
List
之间并没有任何的关系。
但是按照我们通常的思维习惯,Apple和Fruit之间肯定是存在联系,然而编译器却无法识别,那怎么在泛型代码中解决这个问题呢?我们可以通过使用通配符来解决这个问题:
这样就相当与告诉编译器, fruitReader的readCovariant方法接受的参数只要是满足Fruit的子类就行(包括Fruit自身),这样子类和父类之间的关系也就关联上了。
PECS原则
上面我们看到了类似
extends T>
的用法,利用它我们可以从list里面get元素,那么我们可不可以往list里面add元素呢?我们来尝试一下:
答案是否定,Java编译器不允许我们这样做,为什么呢?对于这个问题我们不妨从编译器的角度去考虑。因为
List extends Fruit> flist
它自身可以有多种含义:
-
当我们尝试add一个Apple的时候,flist可能指向
new ArrayList
()
;
-
当我们尝试add一个Orange的时候,flist可能指向
new ArrayList
()
;
-
当我们尝试add一个Fruit的时候,这个Fruit可以是任何类型的Fruit,而flist可能只想某种特定类型的Fruit,编译器无法识别所以会报错。