Ответ состоит из двух частей.Во-первых, генерики стираются во время компиляции .Вторая часть - это фактическая реализация ArrayList
.Давайте начнем с стирания типа.
Во время компиляции все универсальные типы заменяются их верхней границей.Например, общий параметр <T extends Comparable<T>>
сворачивается в Comparable<T>
.Если верхняя граница не указана, она заменяется на Object
.Это делает дженерики эффективным инструментом для проверки типов во время компиляции, но мы теряем всю информацию о типах во время выполнения. Project Valhalla может исправить или не исправить это в будущем.Поскольку ваш метод работает с необработанными и неограниченными типами, компилятор принимает Object
в качестве универсального типа и, таким образом, list1.add("12sdf34");
проходит проверку типов.
Итак, почему бы вам не получить какое-то исключение во время выполнения?Почему ArrayList
не "распознает", что значение, которое вы даете, имеет неправильный тип?Потому что ArrayList
использует Object[]
в качестве резервного буфера .Следующий логический вопрос: почему ArrayList
использует Object[]
вместо T[]
?Из-за стирания типа: мы не можем создать экземпляр T
или T[]
во время выполнения.
Для вашей программы это означает, что ни ошибка компиляции, ни исключение времени выполнения не будут выброшены, и, следовательно, ваш ArrayList<Integer>
может содержать String
.Однако у вас возникнут проблемы, написав
...
System.out.println("Contains? " + (new Wildcards()).contains(list1, list2));
System.out.println("List1 element: " + list1.get(0))
int i = list1.get(0);
С точки зрения лексера, код все еще действителен.Но во время выполнения назначение сгенерирует ClassCastException
.Это одна из причин, по которой необработанных типов следует избегать .