您的位置:首页 > 编程语言 > Java开发

Java 泛型 <? super T> 中 super 怎么 理解<?与 extends> 有何不同?

2016-11-26 17:33 507 查看
Java 泛型 <? super T> 中 super 怎么 理解<?与 extends> 有何不同?

问题引入:

// compile error
// List <? extends Fruit> appList2 = new ArrayList();
// appList2.add(new Fruit());
// appList2.add(new Apple());
// appList2.add(new RedApple());

List <? super Fruit> appList = new ArrayList();
appList.add(new Fruit());
appList.add(new Apple());
appList.add(new RedApple());

看到这个问题,打开eclipse写代码验证一下,得出以下一些结论:

首先解释两个概念:Java中的通配符和边界
通配符: “?”就是一个占位符,它不表示任何具体的类型,而是表示符合定义规则的一个或者多个类型的一个占位标志
边界:
<? extends T> 表示上界通配符 它表示T以及T的子类, 类型最高是T
<? super T> 表示下界通配符 它表示T以及T的超类,类型最高可到Object ,最低是T

<? extends T> 表示上界通配符 它表示T以及T的子类, 类型最高是T

为什么上述问题中使用上界通配符后就出现编译出错的问题:
List<? extends Fruit> list 表示当前列表中可以存储 Fruit以及Fruit的子类, 从这层含义来看,似乎上述不应该报错,因为存储的对象是符合list定义限定数据范围的。 但是事实上确实报错了,这主要是Java出于泛型安全性的考虑。因为上界通配符<? extends T> 表示的是一个类型范围,编译器无法确定List所持有的具体类型,所以无法安全的向其中添加对象。但是可以添加null,因为null
可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List<Apple> 赋值。因此对于上界边界符,Java编译器限制了其存数据。即:<? extends T> 不能存数据,但是可以取数据,而使用get方法取出来的数据只能赋值为T类型的变量。T
value = list.get(0);

<? super T> 下界通配符: 表示T以及T的超类,可以存数据,但是只能取出Object的数据
如:
List<? super apple> list = new ArrayList<fruit>();;
list.add(new apple());
list.add(new redApple());
// list.add(new fruit()); 此处编译报错,

出错分析:
因为list的定义限定存储数据的类型最低是apple,因此对于编译器来说,只有存入低于或者等于apple的数据对象才能认为是数据安全的。所以存入fruit对象时才会报错。

而同样的 如果要在当前list中取出数据时,因为限定的是最低类型,而最高可以达到Object类,所以,在get数据时只能赋值给Object对象。(假设Java中没有Object类的定义,这里也许就不被允许get数据)

以上分析了泛型边界情况在容器中的使用,同样的在类定义中使用的泛型也同样受到相同的限制。
如:定义个盘子类,
public static class Plate<T> {
T item;
public Plate(T t) {
item = t;
}
public void set(T t) {
item = t;
}
public T get() {
return item;
}
}

写法1:
Plate<? extends fruit> plate = new Plate<apple>(new apple());
plate.set(new redApple()); // 编译报错
plate.get();

报错分析:因为plate定义为<? extends fruit>类型,因此不能往里存而只能往外读,所以第二句报错。而第三句没有问题。
但是刚开始出现了一个疑问,为什么第一句构造函数同样是赋值却可以编译通过呢,原因:第一句构造的过程制定了具体的类型“apple” 所以当前构造出来的具体对象是一个放apple的盘子, 而把这个有具体类型的对象赋值给了一个没有具体类型的变量。 所以有具体类型的对象可以写,而没有具体类型的变量却不能够进行写操作。
<
4000
br />
写法2:
Plate<? super apple> plate = new Plate<apple>(new apple());
plate.set(new fruit()); // 编译报错
plate.set(new redApple());
plate.set(new apple());
plate.get().show(); // 编译报错

上述写法中有两处报错的地方
第二句报错的原因:plate限定的类型最低是apple类型,而这里却要存放一个apple的父类fruit 检测到类型不安全,所以报错。
第五句报错原因:对于下边界符的限定 不影响写却影响读,且读获取到的只是Object对象,如果在第五句加一个强制转换,则是可以正常编译且得出正确结果的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: