С самого начала, как программист на Java, мы все знаем, как создавать экземпляры и использовать объекты Collection. Интерфейс List, созданный как конкретный класс, будет выглядеть следующим образом.
| 1 | List myArrayList  =  newArrayList(); | 
 Если myArrayList должен содержать только объекты Integer, то начиная с компилятора Java 5 и далее в соответствии со спецификацией Java Generics эта реализация будет выглядеть следующим образом: 
| 1 | List<Integer> myArrayList = newArrayList<Integer>(); | 
В тех же строках методы, которые принимают или возвращают списки строк, будут изменены из
| 1 | publicList processStrings(ArrayList myStringList); | 
в
| 1 | publicList<String> processStrings(ArrayList<String> myStringList); | 
И они безопасны по типу, поэтому нам не нужно вводить тип для получения элементов объекта списка.
| 1 | String aStringFromMyStringList = myStringList.get(0); //No ClassCastException possible. | 
  Вышеприведенное не скомпилируется, если aStringFromMyStringList объявлен как-либо, кроме String. 
До сих пор мы должны быть довольны тем, как работает объектно-ориентированная Java, но следующий пункт может удивить многих.
  Когда мы используем List<Integer> myArrayList = new ArrayList<Integer>();  означало, что мы должны использовать «Integer» только в ArrayList и NOTHING ELSE.  Погодите, не являются ли дженерики частью ООП, значит, мы не можем применить полиморфизм к этим объектам?  Ответ — нет.  Посмотрим почему. 
  Мы уже видели, как Полиморфизм применяется к базовому типу коллекций, и именно поэтому List<Integer> myArrayList может быть List<Integer> myArrayList как новый ArrayList<Integer>(); 
Но как насчет этого:
| 1 2 3 | classParent{}classChild extendsParent{} | 
Используя вышеупомянутое, инстанцирование не будет работать и в итоге приведет к ошибке компиляции.
| 1 | List<Parent> myList = newArrayList<Child>() //Compilation Error; | 
  Простое правило: тип объявления переменной должен соответствовать типу, который вы передаете фактическому типу объекта.  Если мы объявляем List<Parent> myList то все, что я назначаю myList ДОЛЖНО быть точно только с типом <Parent> а не подтипом класса Parent, а не супертипом класса Parent. 
Это означает, что правильный код:
| 1 | List<Parent> myList = newArrayList<Parent>(); // Compiles fine | 
Но вышесказанное противоречит традиционным Java-программистам, которые привыкли использовать нижеследующее, что является законным.
| 1 | Parent[] myParentArray = newChild[10]; | 
Чтобы понять вышеупомянутое несоответствие в деталях, давайте создадим структуру наследования, как показано ниже:
| 1 2 3 4 5 | publicclassAnimal{}publicclassCat extendsAnimal{}publicclassDog extendsAnimal{} | 
Мы можем реализовать полиморфизм в массивах, поскольку массивы не должны быть безопасными. См. Приведенный ниже пример для массивов и почему нам нужны безопасные списки типов как объекты Collection.
| 1 2 3 4 5 6 7 8 9 | publicvoidaddAnimals(Animal[] animals ) {    animals [0] = newAnimal();             // If passed animal[] is of type Dog[] then we are adding a Cat object to a Dog[] array.    animals [1] = newCat();             // If passed animal[] is of type Cat[] then we are adding a Dog object to a cat[] array.             animals [1] = newDog(); } | 
Поскольку Cat или Dog — это тип Animal, следовательно, Cat Array или Dog Array можно передавать как Animal Array.
| 1 2 3 4 5 6 7 8 9 | publicclasscallerClass() {              Animal[] animalArray = newAnimal[10];              Cat[] catArray = newCat[10];              Dog[] dogArray = newDog[10];             addAnimals(animalArray); //Expected, no questions raised here.  addAnimals(catArray); //As Cat[] is a type of Animal[] so we may end up in adding a Cat in Dog Array.                                                             addAnimals(dogArray); // As Dog[] is a type of Animal[] so if Cat[] is passed we may end up in adding a Dog in a //Cat array.} | 
Но посмотрим, что произойдет, если мы будем использовать Коллекции. У нас может быть похожий метод, как указано выше:
| 1 | publicvoidaddAnimals(List<Animal> myAnimalList()) {     //Some code here.  } | 
Вызывающий метод, который вызывает вышеуказанный метод, будет таким, как показано ниже.
| 1 2 3 4 5 6 7 8 9 | publicclasscallerClass() {              List<Animal> animalList = newArrayList<Animal>();              List<Cat> catList = newArrayList<Cat>();              List<Dog> dogList = newArrayList<Dog>();             addAnimals(animalList);              addAnimals(catList);             addAnimals(dogList); } | 
  Что произойдет, если мы попытаемся скомпилировать вышесказанное?  Это потерпит неудачу в строке addAnimals(catList);  и addAnimals(dogList) , поскольку тип List не совпадает с ожидаемым типом списка метода addAnimals(List<Animal> myAnimalList()) .  Метод ожидает, что список объявлен ТОЛЬКО типом Animal. 
  Несмотря на то, что вышеприведенное не удалось, обобщенные элементы могут фактически содержать экземпляр подтипов, когда список объявляется как список супертипов.  Например, у нас может быть подробная реализация метода addAnimals ( List<Animal> myAnimalList () ), как List<Animal> myAnimalList () ниже. 
| 1 2 3 4 5 | publicvoidaddAnimals(List<Animal> myAnimalList ()) {           aList.add(newAnimal()); // Expected code.           aList.add(newCat()); //Yes this works.           aList.add(newDog()); //Any Animal subtype works.} | 
Это означает, что мы можем применить концепцию наследования суб-суперкласса — ДОБАВЛЕНИЕ объектов в список, но не в присвоении или передаче объектов в качестве аргумента метода.
  И это причина, по которой Java не позволяет addAnimals(catList) код addAnimals(catList) потому что если он скомпилируется, то позже в реализованном методе addAnimals мы всегда можем иметь aList.add(new Dog()) , даже если aList является тип списка кошек, что неправильно!  У нас не может быть объекта Dog, добавленного в список Cat, потому что в списке объявлены ТОЛЬКО объекты Cat (или его подклассы).  Дженерики предназначены для того, чтобы сделать ТИП списков БЕЗОПАСНЫМИ и технически значимыми.  Чтобы принять полиморфные под / суперклассы, мы можем улучшить сигнатуру метода с помощью подстановочных знаков, которые могут обсуждаться в другом сеансе.