我不明白 java 中的通配符

I not understend wildcards in java

我有 类:

class Animal{
    public void type(){
        System.out.println("I am Animal");
    }
}
class Dog extends Animal{
    public void type(){
        System.out.println("I am Dog");
    }
}
class Cat extends Animal{
    public void type(){
        System.out.println("I am Cat");
    }
}
class Haski extends Dog{
    public void type(){
        System.out.println("I am Haski");
    }
}

然后我用 wildcards:

创建了 List
List<? extends Animal> animalList = new ArrayList<Animal>();

我知道我不能向 animalList 添加一些对象。我在不同的书籍、互联网文章、视频课程中读到过它,但我仍然不明白为什么?如果我们认为 animalList 只包含对象 extends Animal 为什么 java 不能添加任何 objects extends Animal 并将其转换为 Animal

animalList.add(new Dog()); //cast dog to Animal
animalList.add(new Cat()); //cast cat to Animal

编译器有足够的信息 - objects extends Animal为什么不能转换?

编辑:

所以我没理解对

List<Animal> animalList1 = new ArrayList<Animal>();
animalList.add(new Animal());
animalList.add(new Dog());
animalList.add(new Cat());
animalList.add(new Haski());

List<? extends Animal> animalList

感觉应该是一样的。但是我感觉不出原理上的区别

因为这个:

List<Dog> dogList = new ArrayList<>(); // compiles
List<? extends Animal> listOfUnknownAnimalType = dogList; // compiles
listOfUnknownAnimalType.add(new Cat()); // doesn't compile

如果编译了第三行,您将能够将 Cat 添加到 List<Dog>,这将完全破坏泛型带来的类型安全:listOfUnknownAnimalType 被初始化为 dogList 在第 2 行。因此两个变量都引用同一个列表,即 List<Dog>。因此,如果将猫添加到 listOfUnknownAnimalType,则将其添加到 dogList。将 Cat 添加到狗的列表中是不正确的:List<Dog> 应该只包含狗,而不是猫。

假设你有这个方法:

public void printAllTypes(List<Animal> list) {
    list.forEach(a -> System.out.println(a.type()));
}

这很好,效果很好:

List<Animal> list = new ArrayList<>();
list.add(new Cat());
list.add(new Dog());
printAllTypes(list);

现在假设你有这个:

List<Dog> list = new ArrayList<>();
list.add(new Haski());
list.add(new Dog());
printAllTypes(list);

最后一行无法编译,因为 List<Dog> 不是 List<Animal>(原因同上)。这就是通配符变得有用的地方:因为你的方法实际上并不关心列表的具体泛型类型,只要它扩展了 Animal,并且因为它不改变列表,你可以将你的方法重写为

public void printAllTypes(List<? extends Animal> list) {
    list.forEach(a -> System.out.println(a.type()));
}

现在您可以使用 List<Animal> 调用它,还可以使用 List<Dog>List<Cat> 作为参数。