了解通配符约束<?超级T>

Skl*_*ogW 2 java generics

我可以使用基本的通用表达式,但是通配符约束只是让我心烦意乱.

信息:学生扩展人和人扩展动物

 List<? super Animal> aList= new ArrayList<>();

       // does not compile as expected, 
       // since the list is restricted to something that has Animal as superclass.
       // aList.add(new Object()); 

    aList.add(new Animal()); // I can add animal
    aList.add(new Person()); // I can add Person
    aList.add(new Student()); // I can add Student

    Animal a = new Animal();
    Animal b = new Animal();
    Person p = new Person();
    Student s = new Student();
    // test
    a = b; //I can assign an animal to another animal
    a = p; // I can assign a person to an animal
    a = s; // I can assign a student to an animal

    Animal animal = aList.get(0); // DOES NOT COMPILE, WHY ?
Run Code Online (Sandbox Code Playgroud)

问题:我不明白为什么最后一项任务不起作用.上面的例子显示,该列表中的任何内容肯定是动物,但我无法从该列表中获取动物.

更具体:当我知道我只能添加Animal作为超类的类型时,为什么我不能期望从Animal类型中删除对象?

事实:我只能添加扩展Animal的对象!我只是试图添加一个汽车对象,它不起作用!

我开始怀疑我的理智,我希望你能帮助我.谢谢

另外:

Object animal = aList.get(0); // works
Run Code Online (Sandbox Code Playgroud)

为什么即使我知道无法添加对象类型,此语句仍然有效?

解决方案:(根据接受的答案)

我误解了意思 <? super Animal>

我认为这意味着:任何具有Animal作为超类的类.

它(显然)意味着什么:任何一类是动物的超类.

因此,List也可能包含Object类型的对象,这就是Animal animal = aList.get(0);失败的原因.

干杯

Bri*_*etz 15

这里的其他答案是正确的,但没有明确说明,这是混乱的来源.

一个List<? extends Animal>不会意味着"的东西,都可以扩展动物名单".它的意思是"某种类型的列表T,我不会告诉你它是什么,但我知道它会T扩展Animal." (在类型理论中,这些被称为存在类型 - 存在一种T我们List是a 的类型List<T>,但我们不一定知道它是什么T.)

差异很重要.如果你有"所有扩展的东西列表Animal",那么将一条狗添加到列表中是安全的 - 但这不是通配符所表达的.A List<? extends Animal>表示"某物的列表,其中某物延伸到动物".它可能是a List<Animal>,或a List<Dog>,或List<Cat>- 我们不知道.因此,我们没有理由认为将狗添加到该列表是安全的 - 因为也许我的列表是一个List<Cat>.

在你的例子中,你有一个List<? super Animal>.这意味着,某些类型T的列表,其中T是Animal的超类型之一.它可能是a List<Object>,或者List<Animal>,或者如果Animal有一个超类型HasLegs,它可能是一个List<HasLegs>.在这里, Dog放入此列表是安全的- 因为无论它的列表是什么,Dog绝对是其中之一(Animal,Object等),但当你列表中取出一些东西,你不知道它是否是狗,动物,或只是一个对象.

  • 当你有一个`List <?扩展Animal>`(可以是List <Animal>,List <Dog>,List <Cat>),你可以_take动物out_,但你不能_put动物in_.当你有一个`List <?超级动物>`(可能是List <Animal>,List <Object>),你可以把动物放进去,但你不能把它们拿走.只有当你有一个不变的清单 - 列出<动物> - 你们都可以把动物拿出来放进去. (4认同)
  • @SklogW可能是因为你仍然在解释通配符错误.`名单<?延伸动物>"不代表"一系列事物,所有这些都扩展了动物." 正是这种共同的直觉导致人们陷入混乱; 一旦你发挥了这种直觉并意识到通配符实际意味着什么,它就更有意义了...... (2认同)

Psh*_*emo 10

List<? super Animal> aList其可以被用来处理是参考List<Animal> ,它也可以是动物的任何超类List<Object>.

换句话说List<? super Animal> aList
- 不是引用一些可以存储任何超类型Animal的列表.
+它某些特定类型的列表的引用,它是Animal(包括Animal本身)的超类型但你不知道它究竟是哪种类型.

所以它可能是

List<Object> someList = new ArrayList<>();
someList.add(new Car());//OK since Car is a Object
List<? super Animal> aList = someList;// OK since Object is supertype of Animal
Run Code Online (Sandbox Code Playgroud)

因此,代码

Animal a = aList.get(0);//and where did that Car come from?
Run Code Online (Sandbox Code Playgroud)

不安全 只有安全型存储的结果get(0)Object让你无论是需要

Object o = aList.get(0);
Run Code Online (Sandbox Code Playgroud)

或者如果您想确保get将返回Animal,请更改您的aList引用类型List<Animal>甚至List<? extends Animal>.

  • 你什么意思?让`List <?super Animal>`引用特定列表,如`List <Animal>`或`List <LeavingThing>`甚至`List <Object>`是`<?的完整目的 超级动物>`因此,简单地假设`List <LeavingThing>`或`List <Object>`只包含`Animals`是不合理的. (2认同)