我正在努力解决这个问题,并且想知道是否有人可以解释原因。
我有三个班级:
class Angel {}
class Person extends Angel {}
class Employee extends Person {}
当我尝试执行此代码时
public static void insertElements(List<? super Person> list){
list.add(new Person());
list.add(new Employee());
list.add(new Angel());
}
我收到错误:
The method add(capture#5-of ? super Person) in the type List<capture#5-of ? super Person> is not applicable for the arguments (Angel)
我一直在阅读文档,意思是<? super X >
意味着任何类型的X或超类X(所以在我的情况下,Angel是Person的超类)并且应该被允许?显然不是!
有没有人有任何简单的例子?
答案 0 :(得分:21)
你的直觉逻辑说'List<? super Person>
是一个列出的Person
或超Person
的东西,所以我很自然地可以在其中添加Angel
&#34;。这种解释是错误的。
声明List<? super Person> list
保证list
将是一种类型,允许将Person
的任何内容添加到列表中。由于Angel
不是Person
,因此编译器自然不允许这样做。考虑使用insertElements(new ArrayList<Person>)
调用您的方法。可以将Angel
添加到这样的列表中吗?绝对不是。
推理它的最好方法是List<? super Person>
不是明确的类型:它是一个模式,用于描述允许作为参数的一系列类型。查看List<Person>
不是List<? super Person>
的子类型,而是匹配此模式的类型。 List<? super Person>
上允许的操作是任何匹配类型允许的操作。
答案 1 :(得分:11)
对我来说,这些答案都不够清楚,尽管他们帮助了我。看了很多,我的意思很多,我终于想出了最简单的通配符解释:
public class CatTest {
public class Animal {}
public class Cat extends Animal {}
public class MyCat extends Cat {}
public class Dog extends Animal {}
public static void main(String[] args) {
List<Animal> animalList = new ArrayList<>();
List<Cat> catList = new ArrayList<>();
List<MyCat> myCatList = new ArrayList<>();
List<Dog> dogList = new ArrayList<>();
CatTest catTest = new CatTest();
// Here you are trying to add a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat, Cat is an Animal, therefore MyCat is an Animal.
// So you can add a new MyCat() to a list of List<Animal> because it's a list of Animals and MyCat IS an Animal.
catTest.addMethod(animalList);
// Here you are trying to add a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat.
// So you can add a new MyCat() to a list of List<Cat> because it is a list of Cats, and MyCat IS a Cat
catTest.addMethod(catList);
// Here you are trying to add a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat.
// Everything should work but the problem here is that you restricted (bounded) the type of the lists to be passed to the method to be of
// a type that is either "Cat" or a supertype of "Cat". While "MyCat" IS a "Cat". It IS NOT a supertype of "Cat". Therefore you cannot use the method
catTest.addMethod(myCatList); // Doesn't compile
// Here you are adding a MyCat instance (in the addMethod we create a MyCat instance). MyCat is a Cat.
// You cannot call the method here, because "Dog" is not a "Cat" or a supertype of "Cat"
catTest.addMethod(dogList); // Doesn't compile
}
public void addMethod(List<? super Cat> catList) {
// Adding MyCat works since MyCat is a subtype of whatever type of elements catList contains
// (for example Cat, Animal, or Object)
catList.add(new MyCat());
System.out.println("Cat added");
}
}
最后,这些是结论:
使用通配符时,通配符应用到类型的列表作为方法的参数传递,不是类型的尝试将元素添加到列表时,元素。在该示例中,您收到以下编译错误:
CatTest类型中的方法addMethod(List)不适用于参数(List)
如您所见,错误是关于方法签名,而不是关于方法的主体。所以你只能传递34; Cat&#34;或超级类型的&#34; Cat&#34; (List<Animal>, List<Cat>
)。
一旦您传递了包含特定类型元素的列表,您就可以仅将元素添加到列表中,这些元素是&#34; Cat&#34;或者&#34; Cat&#34;的子类型,也就是说,当你有一个元素集合时,它表现为一如既往!您无法添加&#34; 动物&#34;到#34; Cat &#34;的列表。正如我之前所说,通配符不会对元素本身应用,这些限制仅适用于&#34; List&#34;。现在,为什么呢?由于简单,明显,众所周知的原因:
Animal animal = new Dog();
如果您可以添加&#34; Animal&#34;到了#34; Cat&#34;列表,您还可以添加一个&#34; Dog&#34; (a&#34; Dog&#34;是&#34; Animal&#34;),但它不是&#34; Cat&#34;。
答案 2 :(得分:1)
我一如既往地认真理解,引入List<? super Person>
是为了确保List<Person>
和List<Angel>
都可以作为参数传递,这就是灵活性upper bounded wildcard提供。
但是在方法内,在您的情况下,只能将Person
或Person
的子类型插入列表,以确保列表始终包含有效实例。
例如如果您要传递Person
的列表,则只能插入Employee
和Person
,而Employee
会自动键入到Person
。
答案 3 :(得分:0)
反过来考虑一下:对于List<? super Person>
上绑定的类型,List<Person>
显然是参数的有效类型。
如果编译器允许您的代码,则允许您将Angel
类型的内容插入List<Person>
。
答案 4 :(得分:0)
通配符基本上允许您指定参数的类型范围,在您编写时的情况
public static void insertElements(List<? super Person> list)
,编译器将允许您传递 Person 或其任何超类型的列表。让我们看一个具体的例子
List<Object> objects = new ArrayList<>();
List<Angel> angels = new ArrayList<>();
List<Person> persons = new ArrayList<>();
List<Employee> employees = new ArrayList<>();
insertElements(objects); // Works fine, Object is super type of every type
insertElements(angels); // Works fine, Angel is super type of Person
insertElements(persons); // Works fine, Person itself
insertElements(employees); // Error, Employee is neither Person itself nor its a super type of Person
现在让我们看看为什么会出现这个错误
从上面的代码我们知道您可以将人员、天使或对象的列表传递给 insertElements
并且它会正常工作,现在假设您使用人员列表调用该方法。所以列表现在是List<Person>
,你做list.add(Angel());
这实质上意味着您正在尝试执行 Person person = new Angel();
此分配与类型不兼容,并且您会收到错误消息。