Java下界通配符

问题描述 投票:13回答:4

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

我有三个班:

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

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

java wildcard
4个回答
21
投票

你的直觉逻辑说“List<? super Person>是一个PersonPerson的超类型的列表,所以我自然可以添加一个Angel”。这种解释是错误的。

声明List<? super Person> list保证list将是这样一种类型,允许任何Person被添加到列表中。由于Angel不是Person,编译器自然不允许这样做。考虑使用insertElements(new ArrayList<Person>)调用您的方法。可以将Angel添加到这样的列表中吗?当然不。

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


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)

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

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

Animal animal = new Dog();

如果您可以将“Animal”添加到“Cat”列表中,您还可以添加“Dog”(“Dog”是“Animal”),但它不是“Cat”。


1
投票

我一直都有严格的理解,引入List<? super Person>以确保List<Person>List<Angel>都可以作为upper bounded wildcard提供的灵活性的参数传递。

但在该方法中,在您的情况下,只能将PersonPerson的子类型插入到列表中,以确保列表始终包含有效实例。

例如。如果你传入Person列表,那么只能插入EmployeePerson,而Employee将自动输入到Person


0
投票

反过来考虑一下:对于List<? super Person>上绑定的类型,List<Person>显然是参数的有效类型。如果编译器允许你的代码,它将允许你将Angel类型的东西插入到List<Person>中。

© www.soinside.com 2019 - 2024. All rights reserved.