通配符背后的目的是什么?它们与泛型有何不同?

时间:2010-05-27 16:13:22

标签: java generics wildcard

我几天前从未听说过野猫,在读完老师的Java书之后,我仍然不确定它是什么,为什么我需要使用它。

假设我有一个超类Animal和几个子类,如DogCatParrot等等...现在我需要一个列表动物,我的第一个想法是:

List<Animal> listAnimals

相反,我的同事们推荐的内容如下:

List<? extends Animal> listAnimals

为什么我应该使用通配符而不是简单的泛型?

假设我需要一个get / set方法,我应该使用前者还是后者?他们怎么这么不同?

4 个答案:

答案 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

  • BA
  • 的子类型
  • 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