В этой части разберём wildcards, и как дженерики работают с иерархиями.
Начнём с ответа на вопрос перед постом. Компилятор выдаст ошибку.
Почему так?
Пойдём от обратного и представим, что метод принимает List<Child>.
Внутри метода мы работаем со ссылкой на List<Parent>, значит в список можно добавить объект Parent. Если метод примет List<Child>, значит List<Child> тоже принимает объекты Parent. Что противоречит контракту, поэтому List<Child> никак не связан с List<Parent>.
Здесь разработчики дженериков учли неудачный опыт с массивами. Для массивов возможна такая ситуация:
🔸 Создаём переменную arr с массивом Parent:
Parent[] arr;🔸 Присваиваем массив Child:
arr = new Child[2];🔸 Добавляем в arr экземпляры Child и Parent:
arr[0] = new Child();Вторая строка в рантайме бросит ArrayStoreException. Дженерики обрабатываются при компиляции, поэтому ошибок на этапе выполнения нет.
arr[1] = new Parent();
Wildcards
Для ситуаций, когда мы точно-точно не будем менять список, и нам только посмотреть, можно использовать wildcards:
void print(List<?> list)
Теперь метод принимает списки любых типов. Конкретный тип неизвестен, поэтому доступны только методы класса Object. Можно получить размер списка или вызвать для элементов toString.
❓Чем List<?> отличается от List?
В List<?> компилятор не даст ничего добавить, а для List ограничений нет.
❓Как вызвать нужные методы для List<?>?
Тип в списке можно уточнить:
void print(List<? extends Parent> list)Теперь метод принимает только List<Parent> и List<Child>, плюс есть доступ к методам Parent. Но менять список всё ещё нельзя.
Буквы и вопросики
Есть два способа ограничить типы списков:
▫️ <T extends Parent> void test(List<T> list)Чем они отличаются?
▫️ void test(List<? extends Parent> list)
T - это конкретный тип элементов списка, поэтому в список можно добавить объекты типа Т. В листе с вопросиком конкретный тип НЕ известен, и компилятор НЕ даст добавить новые элементы.
extends и super
В иерархии Object - Parent - Child можно ограничить типы с разных сторон.
extends - ограничение иерархии "сверху"
? extends Parent: допустимы Parent, Child и наследники Childsuper - ограничение типов "снизу"
? super Parent принимает Parent и родителей Parent вплоть до Object