Java 泛型:列表<? extends Animal> 和 List<Animal> 一样吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2575363/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:24:56  来源:igfitidea点击:

Generics : List<? extends Animal> is same as List<Animal>?

javagenericscovarianceextendscontravariance

提问by peakit

I am just trying to understand the extendskeyword in Java Generics.

我只是想了解extendsJava 泛型中的关键字。

List<? extends Animal>means we can stuff any object in the Listwhich IS AAnimal

List<? extends Animal>意味着我们可以在List其中填充任何对象是 AAnimal

then won't the following also mean the same thing:

那么以下是否也意味着同样的事情:

List<Animal>

Can someone help me know the difference between the above two? To me extendsjust sound redundant here.

有人可以帮我了解上述两者之间的区别吗?对我来说extends只是在这里听起来多余。

Thanks!

谢谢!

采纳答案by Heinzi

List<Dog>is a subtype of List<? extends Animal>, but not a subtype of List<Animal>.

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

Why is List<Dog>not a subtype of List<Animal>? Consider the following example:

为什么List<Dog>不是 的子类型List<Animal>?考虑以下示例:

void mySub(List<Animal> myList) {
    myList.add(new Cat());
}

If you were allowed to pass a List<Dog>to this function, you would get a run-time error.

如果允许List<Dog>您将 a传递给此函数,则会出现运行时错误。



EDIT: Now, if we use List<? extends Animal>instead, the following will happen:

编辑:现在,如果我们List<? extends Animal>改为使用,则会发生以下情况:

void mySub(List<? extends Animal> myList) {
    myList.add(new Cat());     // compile error here
    Animal a = myList.get(0);  // works fine 
}

You couldpass a List<Dog>to this function, but the compiler realizes that adding something to the list could get you into trouble. If you use superinstead of extends(allowing you to pass a List<LifeForm>), it's the other way around.

可以将 a 传递List<Dog>给此函数,但编译器意识到向列表中添加内容可能会给您带来麻烦。如果您使用super而不是extends(允许您传递 a 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
}

The theory behind this is Co- and Contravariance.

这背后的理论是协变和逆变

回答by Progman

It is not. List<Animal>says that the value which is assigned to this variable must be of "type" List<Animal>. This however doesn't mean that there must only be Animalobjects, there can be subclasses too.

它不是。List<Animal>说分配给这个变量的值必须是 "type" List<Animal>。然而,这并不意味着必须只有Animal对象,也可以有子类。

List<Number> l = new ArrayList<Number>();
l.add(4); // autoboxing to Integer
l.add(6.7); // autoboxing to Double

You use the List<? extends Number>construct if you are interest in an list which got Numberobjects, but the List object itself doesn't need to be of type List<Number>but can any other list of subclasses (like List<Integer>).

List<? extends Number>如果您对包含Number对象的列表感兴趣,则可以使用该构造,但 List 对象本身不需要是类型,List<Number>但可以是任何其他子类列表(如List<Integer>)。

This is sometime use for method arguments to say "I want a list of Numbers, but I don't care if it is just List<Number>, it can be a List<Double>too". This avoid some weird down casts if you have a list of some subclasses, but the method expects a list of the baseclass.

这有时用于方法参数,以表示“我想要一个 的列表Numbers,但我不在乎它是否只是List<Number>,它也可以是一个List<Double>。如果您有一些子类的列表,这可以避免一些奇怪的向下转换,但该方法需要一个基类的列表。

public void doSomethingWith(List<Number> l) {
    ...
}

List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // not working

This is not working as you expecting List<Number>, not a List<Double>. But if you wrote List<? extends Number>you can pass List<Double>objects even as they aren't List<Number>objects.

这不像您期望的那样工作List<Number>,而不是List<Double>. 但是如果你写了List<? extends Number>你可以传递List<Double>对象,即使它们不是List<Number>对象。

public void doSomethingWith(List<? extends Number> l) {
    ...
}

List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // works

Note:This whole stuff is unrelated to inheritance of the objects in the list itself. You still can add Doubleand Integerobjects in a List<Number>list, with or without ? extendsstuff.

注意:整个内容与列表本身中对象的继承无关。您仍然可以在列表中添加DoubleInteger对象List<Number>,有或没有? extends东西。

回答by Cam

With List<Animal>, you know what you have is definitely a list of animals. It's not necessary for all of them to actually be exactly 'Animal's - they could also be derived types. For example, if you have a List of Animals, it makes sense that a couple could be Goats, and some of them Cats, etc - right?

有了List<Animal>,你就知道你拥有的绝对是一个动物列表。所有这些都没有必要实际上完全是“动物的”——它们也可以是派生类型。例如,如果您有一个动物列表,那么一对夫妇可能是山羊,其中一些是猫,等等——对吗?

For example this is totally valid:

例如,这是完全有效的:

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

Of course, the following would notbe valid:

当然,以下将不会是有效的:

aL.peek().meow(); // we can't do this, as it's not guaranteed that aL.peek() will be a Cat


With List<? extends Animal>, you're making a statement about the type of listyou're dealing with.

使用List<? extends Animal>,您正在声明您正在处理的列表类型

For example:

例如:

List<? extends Animal> L;

This is actually nota declaration of the type of object L can hold. It's a statement about what kinds of lists L can reference.

这实际上不是L 可以容纳的对象类型的声明。这是关于 L 可以引用哪些类型的列表的声明。

For example, we could do this:

例如,我们可以这样做:

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

But now all the compiler knows about L is that it is a List of [either Animal or a subtype of Animal]s

但是现在编译器所知道的关于 L 的所有信息是它是一个[或者是 Animal 或者是 Animal 的子类型] 的列表

So now the following is not valid:

所以现在以下内容无效:

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

Because for all we know, L could be referencing a list of Goats - to which we cannot add an Animal.

因为就我们所知,L 可能指的是山羊列表——我们不能向其中添加动物。

Here's why:

原因如下:

List<Goat> gL = new List<Goat>(); // fine
gL.add(new Goat()); // fine
gL.add(new Animal()); // compiletime error

In the above, we're trying to cast an Animal as a Goat. That doesn't work, because what if after doing that we tried to make that Animal do a 'headbutt', like a goat would? We don't necessarily know that the Animal can do that.

在上面,我们尝试将 Animal 转换为 Goat。那是行不通的,因为如果这样做之后我们试图让那个 Animal 像山羊一样做“头撞”怎么办?我们不一定知道 Animal 可以做到这一点。