java泛型基类list_泛型:List<? extends Animal>和List<Animal>一样吗?

stackoverflow上看见一篇关于java泛型非常好的讨论,所以今天拿出来简单翻译一下。 正文

Q:

我只想弄清楚java泛型中extends关键字,List extends Animal>就是任何继承Animal的对象都可以插入到这个List中。它和下面这句难道不一样吗?List

。谁能帮我解释下这两种用法的不同吗?在我看来extends有些多余啊!

谢谢!

A1:

List

是List extends Animal>的子类型,但不是List

的子类型。

为什么List

不是List

的子类型呢?先看一下这个例子吧:

void mySub(List myList) {

myList.add(new Cat());

}

如果允许以List

为参数传入这个方法,那么会发生运行时异常。

EDIT:如果换做List extends Animal>为参数,下面的情况会发生;

void mySub(List< ? extends Animal> myList) {

myList.add(new Cat()); // compile error here

Animal a = myList.get(0); // works fine

}

可以以List

为参数传入这个函数,但是编译器会发现这样会给你带来很多麻烦。如果用super代替extends(允许传入List

),情况就完全相反了:

void mySub(List< ? super Animal> myList) {

myList.add(new Cat()); // works fine

Animal a = myList.get(0); // compile error here,

//since the list entry could be a Plant

}

背后的原理是:协变性和逆变性

A2:

它们是不一样的,List

表示它指向的变量值得类型必须是List

类型的,这并不意味着只能添加Animal对象,还可以添加Animal对象的子类。

List l = new ArrayList();

l.add(4); // autoboxing to Integer

l.add(6.7); // autoboxing to Double

如果想构造一个List存储Number对象,并且这个List本身并不需要是List

类型的,可以也是这个List的子类型(比如List

),这个时候可以使用List extends Animal>。

List extends Number>这种方式用在方法参数时的含义是:只要是个Number列表即可,List

类型的也可以;而List

作为参数时,可以避免错误的向上类型转换,比如想获得一个基类类型的List,却传入一个子类类型的List。

publid void doSomethingWith(List l) {

...

}

List d = new ArrayList();

doSomethingWith(d); // not working

上面这段代码不起作用是因为参数的类型是List

而不是List

。如果改成List extends Number>那么传入List

是没有问题的。

publid void doSomethingWith(List< ? extends Number> l) {

...

}

List d = new ArrayList();

doSomethingWith(d); // works

注意:这东西和List中元素的继承关系无关,不管是否使用? extends都可以将Double或Integer添加到List

中。

A3:

看见你已经找到答案了,我还是想补充一下我的理解,希望能帮上忙。

List extends Animal>和List

的区别如下:

List

就是定义了一个动物的列表,它的元素不仅可以是Animal对象,也可以是Animal的派生类。比如,有一个动物列表,一部分是山羊,还有猫咪等,是这样吗?

下面这个例子证明确实是这样的:

List aL= new List();

aL.add(new Goat());

aL.add(new Cat());

Animal a = aL.peek();

a.walk();//assuming walk is a method within Animal

顺便提一下下面这样是不合法的:

aL.peek().meow();//we can't do this, as it's

//not guaranteed that aL.peek() will be a Cat

当然如果确定aL.peek()就是返回一个Cat对象,那么可以这样做:

((Cat)aL.peek()).meow();//will generate a runtime error

//if aL.peek() is not a Cat

至于List extends Animal>是定义了这个List本身的类型,而不是元素的类型。

比如:

List extends Animal> L;

这段代码不是声明L可以拥有什么类型的对象,而是L本身可以指向什么类型的引用。

比如这样:

List aL = new ArrayList();

L = aL;//remember aL is a List of Animals

经过这样赋值后,编译器就知道L是一个Animal或其子类类型的List了。

所以下面这样做是非法的:

L.add(new Animal());//throws a compiletime error

很明显L是指向Goat类型列表的引用,所以没法将Animal类型的对象加入L。

很好,那因为是什么呢?看这里:

List gL = new List();//fine

gL.add(new Goat());//fine

gL.add(new Animal());//compiletime error

上面add方法报错是因为把Animal类型的对象转换为Goat对象。如果那样做是合法的,那么在调用headbutt方法时,我们没法确定这个Animal对象是否有那样的方法,所以在编译期就会报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值