20

我正在努力解决这个问题,并想知道是否有人可以解释其中的原因。

我有三个班级:

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 的超类)并且应该被允许?明显不是!

有没有人有任何简单的例子?

4

5 回答 5

31

您的直观逻辑说“aList<? super Person>是属于 a或超类型的事物的列表PersonPerson,因此我自然可以将 an 添加Angel到其中”。这种解释是错误的。

该声明List<? super Person> list保证list它将属于允许将任何 aPerson添加到列表中的类型。既然Angel is not aPerson,这自然是编译器不允许的。考虑用insertElements(new ArrayList<Person>). Angel在这样的列表中添加一个可以吗?当然不。

推理它的最好方法是它List<? super Person>没有明确的类型:它是一种描述允许作为参数的类型范围的模式。查看List<Person>不是 的子类型List<? super Person>,而是与此模式匹配的类型。允许的操作List<? super Person>是任何匹配类型允许的操作。

于 2012-11-29T10:05:10.907 回答
16

对我来说,这些答案都不够清楚,尽管它们对我有所帮助。在看了很多,我的意思很多之后,我终于想出了通配符最简单的解释:

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)

如您所见,错误与方法签名有关,与方法主体无关。因此,您只能传递“Cat”或“Cat”( List<Animal>, List<Cat>) 的超类型元素列表。

一旦您传递了具有特定类型元素的列表,您只能将元素添加到列表中,这些元素要么是“Cat”,要么是“Cat”的子类型,也就是说,当您有一个元素集合时,它的行为与往常一样!您不能将“动物”添加到“”列表中。正如我之前所说,通配符不适用于元素本身,限制仅适用于“列表”。现在,这是为什么呢?由于简单、明显、众所周知的原因:

Animal animal = new Dog();

如果您可以将“动物”添加到“猫”列表中,您也可以添加“狗”(“狗”是“动物”),但它不是“猫”。

于 2015-11-24T14:38:59.880 回答
2

像我一直做的那样有一个严格的理解,List<? super Person>引入是为了确保List<Person>并且List<Angel>可以作为参数传入,这是上限通配符提供的灵活性。

但是在 method中,在您的情况下,只能将其Person子类型或子类型Person插入到列表中,以确保列表始终包含有效实例。

例如,如果您要传入 , 的列表Person,则只有EmployeePerson可以插入 whileEmployee将被自动类型转换为Person.

于 2019-03-11T06:26:48.403 回答
0

反过来想一想:对于绑定的类型List<? super Person>List<Person>显然是参数的有效类型。如果编译器允许您的代码,它将允许您将某些类型Angel的内容插入到List<Person>.

于 2012-11-29T10:07:22.043 回答
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();此分配与类型不兼容并且您收到错误。

于 2021-06-24T03:33:40.287 回答