我几天前从未听说过野猫,在读完老师的Java书之后,我仍然不确定它是什么,为什么我需要使用它。
假设我有一个超类Animal
和几个子类,如Dog
,Cat
,Parrot
等等...现在我需要一个列表动物,我的第一个想法是:
List<Animal> listAnimals
相反,我的同事们推荐的内容如下:
List<? extends Animal> listAnimals
为什么我应该使用通配符而不是简单的泛型?
假设我需要一个get / set方法,我应该使用前者还是后者?他们怎么这么不同?
答案 0 :(得分:23)
声明局部变量时,通配符没有多大意义,但是在为方法声明参数时它们非常重要。
想象一下你有一个方法:
int countLegs ( List< ? extends Animal > animals )
{
int retVal = 0;
for ( Animal cur : animals )
{
retVal += cur.countLegs( );
}
return retVal;
}
使用此签名,您可以执行此操作:
List<Dog> dogs = ...;
countLegs( dogs );
List<Cat> cats = ...;
countLegs( cats );
List<Animal> zoo = ...;
countLegs( zoo );
但是,如果你这样声明countLegs
:
int countLegs ( List< Animal > animals )
然后在前面的示例中,只编译了countLegs( zoo )
,因为只有该调用具有正确的类型。
答案 1 :(得分:21)
Java泛型是不变的。
假设我们有B extends A
:
B
是A
instanceof B
也是instanceof A
由于Java数组是协变的:
B[]
是A[]
instanceof B[]
也是instanceof A[]
然而,Java泛型是不变的:
List<B>
不是List<A>
instanceof List<B>
不是instanceof List<A>
。通配符用于使其更加灵活,同时保持类型安全。
List<B>
是List<? extends A>
答案 2 :(得分:9)
您的两个示例之间的区别仅在于第一个是通用/一般动物的列表 - 因此您可以向其中添加任何类型的动物,以及Animal类型的子类的任何实例。 (例如,它可以包含一些狗,一些猫,一些豪猪......)而第二个 - List <? extends Animal>
- 将是一个类动物的一个特定子类型的列表。它可以是您选择的任何一个(每次在运行时设置),但只有一个。它将 成为狗的列表,或列出的猫,或一份海龟等等。
答案 3 :(得分:2)
您可以在List<Animal>
中存储狗和猫。这不是需要通配符的地方。
假设您有一种方法可以获取动物清单:
void foo(List<Animal> animals) {
...
}
现在你不能将方法传递给狗的列表 - 它只需要一个List<Animal>
类型的参数。您需要使用通配符才能使该方法接受各种动物列表:List<Animal>
,List<Dog>
,List<Cat>
,...
void foo(List<? extends Animal> animals) {
...
}
见
http://java.sun.com/docs/books/tutorial/extra/generics/wildcards.html