我不明白 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>
作为参数。
我有 类:
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>
作为参数。