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

What's the purpose behind wildcards and how are they different from generics?

几天前我从未听说过野车,在看过我老师的JAVA书后,我仍然不知道它是什么,为什么我需要使用它。

比如说,我有一个超类Animal,还有几个子类,比如DogCatParrot等等。现在我需要一份动物列表,我的第一个想法是:

1
List<Animal> listAnimals

相反,我的同事推荐的是:

1
List<? extends Animal> listAnimals

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

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


当您声明局部变量时,通配符没有多大意义,但是当您声明方法的参数时,通配符确实很重要。

假设你有一个方法:

1
2
3
4
5
6
7
8
9
10
int countLegs ( List< ? extends Animal > animals )
{
   int retVal = 0;
   for ( Animal cur : animals )
   {
      retVal += cur.countLegs( );
   }

   return retVal;
}

有了这个签名,你就可以做到:

1
2
3
4
5
6
7
8
List<Dog> dogs = ...;
countLegs( dogs );

List<Cat> cats = ...;
countLegs( cats );

List<Animal> zoo = ...;
countLegs( zoo );

但是,如果您声明如下:

1
int countLegs ( List< Animal > animals )

然后在前面的示例中,只有countLegs( zoo )会编译,因为只有该调用具有正确的类型。


Java泛型是不变量的。

假设我们有B extends A

  • BA的一个亚型。
  • instanceof B也是instanceof A

因为Java数组是协变的:

  • B[]A[]的一个亚型。
  • instanceof B[]也是instanceof A[]

然而,Java泛型是不变量的:

通配符用于使其更灵活,同时保持类型安全。

  • ListList

工具书类

  • Java教程/泛型
    • 子类型
    • 野猫游戏更有趣

相关问题

  • 有什么简单的方法来解释为什么我不能做List animals = new ArrayList()
  • Java泛型(非)协方差
  • 有什么区别?


你的两个例子之间的区别仅仅是第一个例子是一个普通/普通动物的列表-因此你可以在其中添加任何类型的动物,以及任何类型动物的子类实例。(例如,它可以包含一些狗,一些猫,一些豪猪…)而第二个-List 将是一个特定的类动物亚型列表。它可以是您选择的任何一个(在运行时每次都设置),但只能是一个。它要么是一张狗的清单,要么是一张猫的清单,要么是一张海龟的清单…等。


你可以把狗和猫放在一个List里。这不是需要通配符的地方。

假设您有一种方法,可以获取动物列表:

1
2
3
void foo(List<Animal> animals) {
    ...
}

现在您不能将方法传递给一个狗列表——它只需要一个类型为List的参数。您需要一个通配符使该方法接受各种动物列表:ListListList……

1
2
3
void foo(List<? extends Animal> animals) {
    ...
}

http://java.sun.com/docs/books/tutorial/extra/generics/通配符.html