Подтвердить что ты не робот

Какова цель подстановочных знаков и как они отличаются от дженериков?

Я никогда не слышал о wildcars до нескольких дней назад и после прочтения моей книги преподавателей Java, я все еще не уверен, для чего это и зачем мне нужно ее использовать.

Скажем, у меня есть суперкласс Animal и несколько подклассов, таких как Dog, Cat, Parrot и т.д. Теперь мне нужно иметь список животных, моя первая мысль была бы чем-то как:

List<Animal> listAnimals

Вместо этого мои коллеги рекомендуют что-то вроде:

List<? extends Animal> listAnimals

Почему я должен использовать подстановочные знаки вместо простых дженериков?

Скажем, мне нужно иметь метод get/set, следует ли использовать первый или более поздний? Как они такие разные?

4b9b3361

Ответ 1

Подстановочные знаки не имеют большого смысла, когда вы объявляете локальные переменные, однако они действительно важны, когда вы объявляете параметр для метода.

Представьте, что у вас есть метод:

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 ) скомпилировался бы, потому что только этот вызов имеет правильный тип.

Ответ 2

Генераторы Java являются инвариантными.

Предположим, что B extends A:

  • B является подтипом A
  • a instanceof B также является instanceof A

Так как массивы Java ковариантны:

  • B[] является подтипом A[]
  • a instanceof B[] также является instanceof A[]

Однако, дженерики Java являются инвариантными:

  • List<B> НЕ является подтипом List<A>
  • a instanceof List<B> НЕ является instanceof List<A>.

Подстановочные знаки используются для обеспечения большей гибкости при сохранении безопасности типов.

  • a List<B> является List<? extends A>

Ссылки

Связанные вопросы

Ответ 3

Различие между двумя примерами состоит в том, что первый - это список общих/общих животных - поэтому вы можете добавить к нему любого типа животных и любой экземпляр подкласса типа Animal. (например, он может содержать некоторых собак, некоторых кошек, некоторых дикобразов...), тогда как второй - List <? extends Animal> - будет списком одного конкретного подтипа животного класса. Это может быть любой, который вы выберете (это устанавливается каждый раз во время выполнения), но только один. Это будет либо список собак, либо список кошек, либо список черепах... и т.д.

Ответ 4

Вы можете хранить собак и кошек в List<Animal>. Это не значит, что нужны маски.

Скажем, у вас есть метод, который принимает список животных:

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

Теперь вы не можете передать метод List of Dogs - он принимает только аргумент типа 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