generics:列表<? 扩展Animal>与List <Animal>相同?
我只是想了解Javagenerics中的extends
关键字。
List<? extends Animal>
List<? extends Animal>
意味着我们可以填充List
任何对象是 Animal
那么下面也不会有同样的意思:
List<Animal>
有人能帮我了解上述两者之间的区别吗? 对我来说这只是声音冗余而已。
谢谢!
List<Dog>
是List<? extends Animal>
的子typesList<? extends Animal>
List<? extends Animal>
,但不是List<Animal>
的子types。
为什么List<Dog>
不是List<Animal>
的子types? 考虑下面的例子:
void mySub(List<Animal> myList) { myList.add(new Cat()); }
如果允许您将List<Dog>
传递给此函数,则会出现运行时错误。
编辑:现在,如果我们使用List<? extends Animal>
List<? extends Animal>
反而会发生以下情况:
void mySub(List<? extends Animal> myList) { myList.add(new Cat()); // compile error here Animal a = myList.get(0); // works fine }
你可以通过一个List<Dog>
到这个函数,但是编译器意识到添加一些东西给列表可能会让你陷入困境。 如果你使用super
而不是extends
(允许你传递一个List<LifeForm>
),那么List<LifeForm>
就是这样。
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 }
这背后的理论是合作和反变换 。
我看到你已经接受了一个答案,但是我只想加上我的意见,因为我想我可以在这里得到一些帮助。
List<Animal>
和List<? extends Animal>
的区别List<? extends Animal>
List<? extends Animal>
如下。
随着List<Animal>
,你知道你有什么是绝对的动物列表。 实际上它们并不一定完全是“动物” – 它们也可以是派生types。 例如,如果你有一个动物列表,那么一对夫妇可能是山羊,其中一些是猫,等等 – 是吗?
例如,这是完全有效的:
List<Animal> aL= new List<Animal>(); 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
用List<? extends Animal>
List<? extends Animal>
,你正在做一个关于你正在处理的列表types的声明。
例如:
List<? extends Animal> L;
这实际上不是对象L可以容纳的types的声明。 这是关于什么样的列表L可以参考的声明。
例如,我们可以这样做:
L = aL; // remember aL is a List of Animals
但是现在所有的编译器都知道L是一个[动物或动物亚型的列表]
所以现在以下是无效的:
L.add(new Animal()); // throws a compiletime error
因为我们所知道的,L可能是参考一个山羊名单 – 我们不能添加一个动物。
原因如下:
List<Goat> gL = new List<Goat>(); // fine gL.add(new Goat()); // fine gL.add(new Animal()); // compiletime error
在上面,我们试图把一只动物当成一只山羊。 那是行不通的,因为如果这样做之后我们试图让那只动物像山羊一样做一个“头撞”呢? 我们不一定知道动物可以做到这一点。
不是这样。 List<Animal>
表示赋给这个variables的值必须是“type” List<Animal>
。 然而,这并不意味着只能有Animal
对象,也可以有子类。
List<Number> l = new ArrayList<Number>(); l.add(4); // autoboxing to Integer l.add(6.7); // autoboxing to Double
你使用List<? extends Number>
如果您对获取Number
对象的列表感兴趣,但是List对象本身不需要是List<Number>
types,而是可以是任何其他子类列表(如List<Integer>
),那么List<? extends Number>
构造。
这有时用方法参数来说“我想要一个Numbers
列表,但我不在乎它是否只是List<Number>
,它也可以是List<Double>
。 如果你有一些子类的列表,这可以避免一些奇怪的下注,但是这个方法需要一个基类列表。
public void doSomethingWith(List<Number> l) { ... } List<Double> d = new ArrayList<Double>(); doSomethingWith(d); // not working
这不是因为您期望List<Number>
,而不是List<Double>
。 但是如果你写了List<? extends Number>
List<? extends Number>
,即使它们不是List<Number>
对象,也可以传递List<Double>
List<Number>
对象。
public void doSomethingWith(List<? extends Number> l) { ... } List<Double> d = new ArrayList<Double>(); doSomethingWith(d); // works
注意:这整个东西与列表本身中的对象的inheritance无关。 你还可以在List<Number>
列表中添加Double
和Integer
对象,有没有? extends
? extends
东西。