javajava 泛型接口extends问题

关于java泛型中的? 、Object、? extends Object的区别 - 开源中国社区
当前访客身份:游客 [
当前位置:
public static void set(List&?& list)
public static void set(List&Object& list)
public static void set(List&? extends Object& list)
请问,这三种,有什么区别?
共有4个答案
<span class="a_vote_num" id="a_vote_num_
List&?& list 可以赋值任何类型,但是不能添加具体的类型
List&Object& list 只能赋值List&Object&,但是可以添加任何类型
List&?extends Object& list 与&List&?& list没区别
--- 共有 1 条评论 ---
(1年前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
因为List&Object&是List&?&的子类,所以List&? extends&Object& list 和&List&?& list是相同的。
<span class="a_vote_num" id="a_vote_num_
1表示使用泛型,不特指对象类,对非泛型而言;
2指定泛型对象类,如果把Object改为其它对象类明显能看出和1的区别;
3泛型对象类只能是继承指定对象的子类。
<span class="a_vote_num" id="a_vote_num_
引用来自“xesam”的评论
List&?& list 可以赋值任何类型,但是不能添加具体的类型
List&Object& list 只能赋值List&Object&,但是可以添加任何类型
List&?extends Object& list 与&List&?& list没区别
更多开发者职位上
有什么技术问题吗?
西夏一品堂的其它问题
类似的话题> JAVA泛型的? extends跟? super的比较
JAVA泛型的? extends跟? super的比较
ouxiaom & &
发布时间: & &
浏览:96 & &
回复:0 & &
悬赏:0.0希赛币
JAVA泛型的
super的比较在JDK1.5之后可以使用泛型,通常可以在下面的地方使用.
A,泛型类声明,如public class GeneralT&A&
B,泛型接口声明,如public interface GenIntf&A&
C,泛型方法声明,如
public &T extends BaseCls& void thisIsT(List&T& list, T e) {
&& &list.add(e);
D,泛型构造器(constructor)声明,如 GeneralT&BaseCls& pgt = new GeneralT&BaseCls&();
在这里主要介绍一下
super的区别.
extends叫做向上造型.
ArrayList&
extends BaseCls& list1 = new ArrayList&BaseCls&();
意味着这个list1里面放的都是BaseClse的子类,保证你可以通过list.get(index)
得到的类都是BaseCls或者BaseCls的子类.
BaseCls cls = list1.get(0);//合法的
list1.add(new BaseCls());或者list1.add(new CldCls());都是不合法的.
这里面是不能通过add函数放东西进去的,那这样有什么用呢.
一般来讲,定义成
extends BaseCls的参数通常只能用来从里面取数据.
如下downbound方法可以接受
extends BaseCls类型的list
public void downbound(List&
extends BaseCls& list) {&& &
&& &for(BaseCls cls:list){
&& &&& &cls.func();
&& &}&& &&& &
ArrayList&BaseCls& list1 = new ArrayList&BaseCls&();
ArrayList&CldCls& list2 = new ArrayList&CldCls&();
downbound(list1);和downbound(list2);都是合法的.
super BaseCls叫做向下造型.
ArrayList&
super BaseCls& list2 = new ArrayList&BaseCls&();
意味着这个list2里面只能放BaseClse或者它的子类.
list2.add(new BaseCls());或者list2.add(new CldCls());都是合法的.
list2.get(index)返回的是Object类型,因为不知道是那个具体类.
限制了放进去的对象的类型,为什么取的时候不是BaseCls呢,这里不理解...
public void upperbound(List&
super BaseCls& list) {
&& &list.add(new BaseCls()
&& &list.add(new CldCls());
本问题标题:
本问题地址:
温馨提示:本问题已经关闭,不能解答。
暂无合适的专家
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&Java泛型继承的问题,有高手可以解答下吗?
[问题点数:40分]
Java泛型继承的问题,有高手可以解答下吗?
[问题点数:40分]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。Posts - 2684,
Articles - 0,
Comments - 33
20:21 by 沧海一滴, ... 阅读,
在本文的前面的部分里已经说过了泛型类型的子类型的不相关性。但有些时候,我们希望能够像使用普通类型那样使用泛型类型:
向上造型一个泛型对象的引用
例如,假设我们有很多箱子,每个箱子里都装有不同的水果,我们需要找到一种方法能够通用的处理任何一箱水果。更通俗的说法,A是B的子类型,我们需要找到一种方法能够将C&A&类型的实例赋给一个C&B&类型的声明。
为了完成这种操作,我们需要使用带有通配符的扩展声明,就像下面的例子里那样:
List&Apple& apples = new ArrayList&Apple&();
List&? extends Fruit& fruits =
&? extends&是泛型类型的子类型相关性成为现实:Apple是Fruit的子类型,List&Apple& 是 List&? extends Fruit& 的子类型。
向下造型一个泛型对象的引用
现在我来介绍另外一种通配符:? super。如果类型B是类型A的超类型(父类型),那么C&B& 是 C&? super A& 的子类型:
List&Fruit& fruits = new ArrayList&Fruit&();
List&? super Apple& =
为什么使用通配符标记能行得通?
原理现在已经很明白:我们如何利用这种新的语法结构?
让我们重新看看这第二部分使用的一个例子,其中谈到了Java数组的子类型相关性:
Apple[] apples = new Apple[1];
Fruit[] fruits =
fruits[0] = new Strawberry();
就像我们看到的,当你往一个声明为Fruit数组的Apple对象数组里加入Strawberry对象后,代码可以编译,但在运行时抛出异常。
现在我们可以使用通配符把相关的代码转换成泛型:因为Apple是Fruit的一个子类,我们使用? extends 通配符,这样就能将一个List&Apple&对象的定义赋到一个List&? extends Fruit&的声明上:
List&Apple& apples = new ArrayList&Apple&();
List&? extends Fruit& fruits =
fruits.add(new Strawberry());
这次,代码就编译不过去了!Java编译器会阻止你往一个Fruit list里加入strawberry。在编译时我们就能检测到错误,在运行时就不需要进行检查来确保往列表里加入不兼容的类型了。即使你往list里加入Fruit对象也不行:
fruits.add(new Fruit());
你没有办法做到这些。事实上你不能够往一个使用了? extends的数据结构里写入任何的值。
原因非常的简单,你可以这样想:这个? extends T 通配符告诉编译器我们在处理一个类型T的子类型,但我们不知道这个子类型究竟是什么。因为没法确定,为了保证类型安全,我们就不允许往里面加入任何这种类型的数据。另一方面,因为我们知道,不论它是什么类型,它总是类型T的子类型,当我们在读取数据时,能确保得到的数据是一个T类型的实例:
Fruit get = fruits.get(0);
使用 ? super 通配符一般是什么情况?让我们先看看这个:
List&Fruit& fruits = new ArrayList&Fruit&();
List&? super Apple& =
我们看到fruits指向的是一个装有Apple的某种超类(supertype)的List。同样的,我们不知道究竟是什么超类,但我们知道Apple和任何Apple的子类都跟它的类型兼容。既然这个未知的类型即是Apple,也是GreenApple的超类,我们就可以写入:
fruits.add(new Apple());
fruits.add(new GreenApple());
如果我们想往里面加入Apple的超类,编译器就会警告你:
fruits.add(new Fruit());
fruits.add(new Object());
因为我们不知道它是怎样的超类,所有这样的实例就不允许加入。
从这种形式的类型里获取数据又是怎么样的呢?结果表明,你只能取出Object实例:因为我们不知道超类究竟是什么,编译器唯一能保证的只是它是个Object,因为Object是任何Java类型的超类。
存取原则和PECS法则
总结 ? extends 和 the ? super 通配符的特征,我们可以得出以下结论:
这就是Maurice Naftalin在他的这本书中所说的存取原则,以及Joshua Bloch在他的这本书中所说的PECS法则。
Bloch提醒说,这PECS是指&Producer Extends, Consumer Super&,这个更容易记忆和运用。
/14985.html
什么是PECS?&
PECS指&Producer Extends,Consumer Super&。换句话说,如果参数化类型表示一个生产者,就使用&? extends T&;如果它表示一个消费者,就使用&? super T&,可能你还不明白,不过没关系,接着往下看好了。
下面是一个简单的Stack的API接口:
public class& Stack&E&{
&&&&public Stack();
&&&&public void push(E e):
&&&&public E pop();
&&&&public boolean isEmpty();
假设想增加一个方法,按顺序将一系列元素全部放入Stack中,你可能想到的实现方式如下:
public void pushAll(Iterable&E& src){
&&&&for(E e : src)
&&&&&&&&push(e)
假设有个Stack&Number&,想要灵活的处理Integer,Long等Number的子类型的集合
Stack&Number& numberStack = new Stack&Number&();
Iterable&Integer& integers = ....;
numberStack.pushAll(integers);
此时代码编译无法通过,因为对于类型Number和Integer来说,虽然后者是Number的子类,但是对于任意Number集合(如List&Number&)不是Integer集合(如List&Integer&)的超类,因为泛型是不可变的。
幸好java提供了一种叫有限通配符的参数化类型,pushAll参数替换为&E的某个子类型的Iterable接口&:
public void pushAll(Iterable&? extends E& src){
&&&&for (E e: src)
&&&&&&&&push(e);
这样就可以正确编译了,这里的&? extends E&就是所谓的 producer-extends。这里的Iterable就是生产者,要使用&? extends E&。因为Iterable&? extends E&可以容纳任何E的子类。在执行操作时,可迭代对象的每个元素都可以当作是E来操作。
与之对应的是:假设有一个方法popAll()方法,从Stack集合中弹出每个元素,添加到指定集合中去。
public void popAll(Collection&E& dst){
&&&&&&&if(!isEmpty()){
&&&&&&&&&&&&&&&&dst.add(pop());
假设有一个Stack&Number&和Collection&Object&对象:
Stack&Number& numberStack = new Stack&Number&();
Collection&Object& objects = ...;
numberStack.popAll(objects);
同样上面这段代码也无法通过,解决的办法就是使用Collection&? super E&。这里的objects是消费者,因为是添加元素到objects集合中去。使用Collection&? super E&后,无论objects是什么类型的集合,满足一点的是他是E的超类,所以不管这个参数化类型具体是什么类型都能将E装进objects集合中去。
注:此文根据《》以及&整理成文。想了解更多有关泛型相关知识,请读者阅读《Effective Java》的第五章。
/8966.html
泛型是在Java 1.5中被加入了,这里不讨论泛型的细节问题,这个在Thinking in Java第四版中讲的非常清楚,这里要讲的是super和extends关键字,以及在使用这两个关键字的时候为什么会不同的限制。&&& 首先,我们定义两个类,A和B,并且假设B继承自A。下面的代码中,定义了几个静态泛型方法,这几个例子随便写的,并不是特别完善,我们主要考量编译失败的问题:&
public class Generic{
public static &T extends A& void get(List&T extends A& list)
list.get(0);
public static &T extends A& void set(List&T extends A& list, A a)
list.add(a);
public static &T super B& void get(List&T super B& list)
list.get(0);
public static &T super B& void set(List&T super B& list, B b)
list.add(b);
编译之后,我们会发现,方法二和方法三没有办法通过编译。按照Thinking in Java上的说法,super表示下界,而extends表示上界,方法二之所以没有办法通过,是因为被放到List里面去的可能是A,也可能是任何A的子类,所以编译器没有办法确保类型安全。而方法三之所以编译失败,则是因为编译器不知道get出来的是B还是B的其他的什么子类,因为set方法四允许在list放入B,也允许在list中放入B的子类,也就没有办法保证类型安全。&& 上面的这段解释听起来可能有点奇怪,都是因为编译器无法判断要获取或者设置的是A和B本身还是A和B的其他的子类才导致的失败。那么Java为什么不干脆用一个关键字来搞定呢?&& 如果从下面的角度来解释,就能把这个为什么编译会出错的问题解释的更加的直白和清除,也让人更容易理解,先看下面的代码,还是A和B两个类,B继承自A:&
public class Generic2{
public static void main(String[] args){
List&? extends A& list1 = new ArrayList&A&();
List&? extends A& list2 = new ArrayList&B&();
List&? super B& list3 = new ArrayList&B&();
List&? super B& list4 = new ArrayList&A&();
&& 从上面这段创建List的代码我们就更加容易理解super和extends关键字的含义了。首先要说明的一点是,Java强制在创建对象的时候必须给类型参数制定具体的类型,不能使用通配符,也就是说new ArrayList&? extends A&(),new ArrayList&?&()这种形式的初始化语句是不允许的。&&& 从上面main函数的第一行和第二行,我们可以理解extends的含义,在创建ArrayList的时候,我们可以指定A或者B作为具体的类型,也就是,如果&? extends X&,那么在创建实例的时候,我们就可以用X或者扩展自X的类为泛型参数来作为具体的类型,也可以理解为给?号指定具体类型,这就是extends的含义。&&& 同样的,第三行和第四行就说明,如果&? super X&,那么在创建实例的时候,我们可以指定X或者X的任何的超类来作为泛型参数的具体类型。&&& 当我们使用List&? extends X&这种形式的时候,调用List的add方法会导致编译失败,因为我们在创建具体实例的时候,可能是使用了X也可能使用了X的子类,而这个信息编译器是没有办法知道的,同时,对于ArrayList&T&来说,只能放一种类型的对象。这就是问题的本质。而对于get方法来说,由于我们是通过X或者X的子类来创建实例的,而用超类来引用子类在Java中是合法的,所以,通过get方法能够拿到一个X类型的引用,当然这个引用可以指向X也可以指向X的任何子类。&&& 而当我们使用List&? super X&这种形式的时候,调用List的get方法会失败。因为我们在创建实例的时候,可能用了X也可能是X的某一个超类,那么当调用get的时候,编译器是无法准确知晓的。而调用add方法正好相反,由于我们使用X或者X的超类来创建的实例,那么向这个List中加入X或者X的子类肯定是没有问题的(?超类有多个,编译器怎么知道是哪个真超类),因为超类的引用是可以指向子类的。&& 最后还有一点,这两个关键字的出现都是因为Java中的泛型没有协变特性的倒置的。

我要回帖

更多关于 java 泛型接口 的文章

 

随机推荐