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

时间:2015-06-24 16:55:39

标签: 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 ?

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

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

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

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

此外:

Object animal = aList.get(0); // works

为什么即使我知道我无法添加对象类型,此语句也能正常工作?

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

我误解了<? super Animal>

的含义

我认为这意味着:任何将Animal作为超类的类。

它(显然)意味着什么:任何属于Animal的超类的类。

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

干杯

3 个答案:

答案 0 :(得分:15)

这里的其他答案是对的,但没有说清楚,这就是混乱的来源。

List<? extends Animal> 表示&#34;所有扩展Animal的内容列表。&#34;这意味着&#34;某种类型T的列表,我不会告诉你它是什么,但我知道T延伸Animal。&#34; (在类型理论中,这些被称为存在类型 - 存在T类型为List的类型List<T>,但我们并不是T。我必须知道Animal是什么。)

差异很重要。如果你有&#34;所有扩展List<? extends Animal>&#34;的内容列表,那么将Dog添加到列表中是安全的 - 但这不是通配符所表达的。 List<Animal>表示&#34;某些内容的列表,其中某些内容扩展了Animal&#34;。它可能是List<Dog>,或List<Cat>List<Cat> - 我们不知道。所以我们没有理由认为将狗添加到该列表是安全的 - 因为我的列表可能是List<? super Animal>

在您的示例中,您有一个List<Object>。这意味着,某些类型T的列表,其中T是Animal的超类型之一。它可能是List<Animal>List<HasLegs>,或者如果Animal有超类型HasLegs,则可能是transform: translate3d(0, 0, 0) rotateY(90deg); 。在这里,狗放入此列表是安全的 - 因为无论它的列表是什么,Dog绝对是其中之一(动物,物体等),但当你拿东西时 out 列表,你不知道它是狗,动物还是只是一个对象。

答案 1 :(得分:10)

List<? super Animal> aList是可用于处理List<Animal> 的引用,它也可以是动物 的任何超类,例如{{1} }。

换句话说List<Object>
  - 引用某些列表,可以存储动物的任何超类型。
 + 引用某些特定类型列表,它是List<? super Animal> aList的超类型(包括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

不安全。只有存储Animal a = aList.get(0);//and where did that Car come from? 结果的安全类型为get(0),因此您需要

Object

或者如果您想确保Object o = aList.get(0); 将返回get,请将Animal引用的类型更改为aList甚至List<Animal>。< / p>

答案 2 :(得分:2)

List<? super Animal>表示Animal的任何超类的列表。如果AnimalLivingThing的子类,则List<? super Animal>可以是Animal的列表,LivingThing的列表或{{1}的列表1}}。在最后两种情况下,作业Object当然是非法的,因为您无法将Animal x = aList.get(0)分配给LivingThing类型的引用。

考虑以下方法(它不会编译):

Animal

方法调用

void f(List<? super Animal> list) {
    Animal a = list.get(0); // line 1
    list.add(new Object()); // line 2
}

是正确的,因为List<Object> list = new ArrayList<>(); list.add(new Object()); f(list); Object的超类。但是,如果方法Animal将进行编译,您将尝试在第1行中将f分配给Object引用,因此它不是类型安全的。

另一方面,请考虑方法调用:

Animal

如果方法f(new List<Animal>()); 会编译,我们现在将f实例添加到一个只应包含Object的列表中。

这就是为什么不允许第1行和第2行。如果你有一个班级

Animal

然后对于类型public class A<T> { public put(T in) { } public T get() { } } ,方法A<? super Animal>将返回get(而非Object),而方法Animal则需要类型参数put