关键字说明
● ? 通配符类型
● <? 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》
● ? 通配符类型
● <? 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;为什么不报错呢?
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文件后他就会自动把泛型去掉,任何同类型的集合的字节码是一个,你可以通过反射给他注入你行要得值
当是成员变量的时候,他一直保持的是编译时类型
而当是成员方法时,他保持的是运行时类型
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来表示吗?
也即:
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