Я вижу, что вы уже приняли ответ, но я просто хотел бы добавить свой взгляд на него, так как я думаю, что могу быть здесь полезным.
Разница между List<Animal>
и List<? extends Animal>
заключается в следующем.
С List<Animal>
вы знаете, что у вас есть определенно список животных. Не обязательно, чтобы все они были точно «животными» - они также могут быть производными типами. Например, если у вас есть список животных, имеет смысл, что пара может быть козой, а некоторые из них - кошками и т. Д. - верно?
Например, это полностью верно:
List<Animal> aL= new List<Animal>();
aL.add(new Goat());
aL.add(new Cat());
Animal a = aL.peek();
a.walk(); // assuming walk is a method within Animal
Конечно, следующее не будет действительным:
aL.peek().meow(); // we can't do this, as it's not guaranteed that aL.peek() will be a Cat
С List<? extends Animal>
вы делаете заявление о типе списка , с которым имеете дело.
Например:
List<? extends Animal> L;
На самом деле не объявление типа объекта L может удерживать . Это утверждение о том, на какие списки L может ссылаться.
Например, мы могли бы сделать это:
L = aL; // remember aL is a List of Animals
Но теперь все, что знает компилятор о L, это то, что это Список [Animal или подтип Animal] s
Так что теперь следующее не действует:
L.add(new Animal()); // throws a compiletime error
Потому что, насколько нам известно, L мог ссылаться на список Коз - к которому мы не можем добавить Животное.
Вот почему:
List<Goat> gL = new List<Goat>(); // fine
gL.add(new Goat()); // fine
gL.add(new Animal()); // compiletime error
Выше мы пытаемся разыграть животное как козу. Это не работает, потому что, если после этого мы попытаемся заставить этого Животного сделать «удар головой», как это сделал бы козел? Мы не обязательно знаем, что Животное может это сделать.