Java泛型通配符<? extends T>与<? super T>

关键字说明  
  
●  ? 通配符类型  
  
●  <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类  
  
●  <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object  
  
extends 示例  
  
    static class Food{}  
    static class Fruit extends Food{}  
    static class Apple extends Fruit{}  
    static class RedApple extends Apple{}  
  
    List<? extends Fruit> flist = new ArrayList<Apple>();  
    // complie error:  
    // flist.add(new Apple());  
    // flist.add(new Fruit());  
    // flist.add(new Object());  
    flist.add(null); // only work for null   
List<? extends Frut> 表示 “具有任何从Fruit继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List<Apple> 赋值。  
  
Fruit fruit = flist.get(0);  
  
Apple apple = (Apple)flist.get(0);  
  
由于,其中放置是从Fruit中继承的类型,所以可以安全地取出Fruit类型。  
  
flist.contains(new Fruit());  
  
flist.contains(new Apple());  
  
在使用Collection中的contains 方法时,接受Object 参数类型,可以不涉及任何通配符,编译器也允许这么调用。  
  
super 示例  
  
List<? super Fruit> flist = new ArrayList<Fruit>();  
  
flist.add(new Fruit());  
  
flist.add(new Apple());  
  
flist.add(new RedApple());  
  
// compile error:  
  
List<? super Fruit> flist = new ArrayList<Apple>();  
  
List<? super Fruit> 表示“具有任何Fruit超类型的列表”,列表的类型至少是一个 Fruit 类型,因此可以安全的向其中添加Fruit 及其子类型。由于List<? super Fruit>中的类型可能是任何Fruit 的超类型,无法赋值为Fruit的子类型Apple的List<Apple>.  
  
// compile error:  
  
Fruit item = flist.get(0);  
  
因为,List<? super Fruit>中的类型可能是任何Fruit 的超类型,所以编译器无法确定get返回的对象类型是Fruit,还是Fruit的父类Food 或 Object.  
  
小结  
  
extends 可用于的返回类型限定,不能用于参数类型限定。  
  
super 可用于参数类型限定,不能用于返回类型限定。  
  
带有super超类型限定的通配符可以向泛型对易用写入,带有extends子类型限定的通配符可以向泛型对象读取。——《Core Java》 





================================================================================================================================
1.问题如下:
在学习java泛型时 ,看到了泛型的一个例子

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
fruits.add(new Strawberry());

其中,Fruit是父类、Apple和Strawberry是子类,add操作执行错误,给出的解释是

“这个? extends T 通配符告诉编译器我们在处理一个类型T的子类型,但我们不知道这个子类型究竟是什么。因为没法确定,为了保证类型安全,我们就不允许往里面加入任何这种类型的数据。”

我不明白编译器怎么会不知道这个子类型是什么呢?如果不知道的话,语句List<? extends Fruit> fruits = apples;为什么不报错呢?


2.问题答案
答案1
这道题应该是编译时类型和运行时类型的区别

当是成员变量的时候,他一直保持的是编译时类型
而当是成员方法时,他保持的是运行时类型

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;    //这个fruits = apples保持的是编译时类型时类型 ,把List<Apple>赋值给 List<? extends Fruit>当然没问题 
fruits.add(new Strawberry());     //这个fruits.add()保持的是运行时类型,fruits运行时他的类型是List<Apple>当然运行出错了,但是在编译时就错误了,更别提运行时了,编译时编译器不会把List的泛型去掉,他会跟着List<? extends Fruit>去判断,所以你编译都过不了,当编译成class文件后他就会自动把泛型去掉,任何同类型的集合的字节码是一个,你可以通过反射给他注入你行要得值

答案2
List<? extends Fruit> fruits = apples;只表示fruits这个变量可以引用apples;
也即:
List<Strawberry> strawberrys = new ArrayList<Strawberry>();
List<? extends Fruit> fruits = strawberrys;
也是可以的;

其实编译器根据 ? extends Fruit 只知道:list中可以是Fruit或它的任何某种子类型对象,但具体是什么类型是不确定的,所以你add(new Fruit()); add(new Strawberry()); add(new Apple()); 编译器都是会报错的;

如果难理解,反过来理解:
比如fruits.add(new Strawberry()),这里是add一个Strawberry类型对象,而fruits要求add的对象类型必须是Fruit或它的任何某种子类型对象,这个某种类型能用Strawberry或Fruit或Apple来表示吗


http://bbs.csdn.net/topics/390054281
http://blog.csdn.net/qq_27789551/article/details/52274927

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值