我可以使用基本的通用表达式,但是通配符约束只是让我心烦意乱。
信息:学生扩展人和人扩展动物
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);
失败的原因。
干杯
答案 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
的任何超类的列表。如果Animal
是LivingThing
的子类,则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
。