Давайте предположим эту иерархию:
class Creature{}// X
class Animal extends Creature{}// Y
class Fish extends Animal{}// Z
class Shark extends Fish{}// A
class HammerSkark extends Shark{}// B
class DeadHammerShark extends HammerSkark{}// C
Давайте уточним PE - Производитель расширяет:
List<? extends Shark> sharks = new ArrayList<>();
Почему вы не можете добавить объекты, которые расширяют "Акула" в этом списке? как:
sharks.add(new HammerShark());//will result in compilation error
Поскольку у вас есть список, который может иметь тип A, B или C во время выполнения , вы не можете добавить в него любой объект типа A, B или C, потому что вы можете получить комбинацию, которая не допускается в Java.
На практике, компилятор действительно может видеть во время компиляции, что вы добавляете B:
sharks.add(new HammerShark());
... но он не может определить, будет ли во время выполнения ваш B подтипом или супертипом типа списка. Во время выполнения тип списка может быть любым из типов A, B, C. Таким образом, вы не можете в конечном итоге добавить HammerSkark (супертип), например, в список DeadHammerShark.
* Вы скажете: «Хорошо, но почему я не могу добавить в него HammerSkark, так как это самый маленький тип?».
Ответ: Это самое маленькое , которое вы знаете. Приобретение HammerSkark может быть продлено кем-то другим, и вы окажетесь в том же сценарии.
Уточним CS - Consumer Super:
В той же иерархии мы можем попробовать это:
List<? super Shark> sharks = new ArrayList<>();
Что и почему вы можете добавить в этот список?
sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());
Вы можете добавить вышеуказанные типы объектов, потому что все, что находится ниже акулы (A, B, C), всегда будет подтипом чего-либо, что находится выше акулы (X, Y, Z). Легко понять.
Вы не можете добавлять типы выше Shark, потому что во время выполнения тип добавленного объекта может быть выше в иерархии, чем объявленный тип списка (X, Y, Z). Это не разрешено.
Но почему вы не можете читать из этого списка?
(Я имею в виду, что вы можете извлечь из него элемент, но вы не можете назначить его чему-либо, кроме объекта o):
Object o;
o = sharks.get(2);// only assignment that works
Animal s;
s = sharks.get(2);//doen't work
Во время выполнения тип списка может быть любого типа выше A: X, Y, Z, ...
Компилятор может скомпилировать ваш оператор присваивания (который кажется правильным), но во время выполнения тип s (Animal) может быть ниже в иерархии, чем объявленный тип списка (который может быть Creature или выше) , Это не разрешено.
Подводя итог
Мы используем <? super T>
для добавления объектов типов, равных или ниже T в список. Мы не можем читать из
это.
Мы используем <? extends T>
для чтения объектов типов, равных или ниже T из списка. Мы не можем добавить элемент к нему.