Важной особенностью дженериков является то, что они инвариантны.В вашем случае это означает, что хотя класс Human расширяет LivingThingTypes
, но List<Human>
не расширяет List<LivingThingTypes>
.Таким образом, в основном вы не можете выполнить следующее задание:
List<Human> list1;
List<LivingThingTypes> list2 = list1; //compile error
Вот как создаются Дженерики.Причина в том, что у вас есть родительский класс Animal, а Dog и Cat - дочерние классы.Теперь вы создали List<Dog>
и присвоили ему List<Animal>
.Теперь, поскольку Animal является родительским классом Cat, вы также можете добавить кошек к List<Animal>
.Хитрая итерация может дать ClassCastException
.Теперь основные плюсы использования Generics в том, что они обеспечивают безопасность типов, но получение ClassCastException
означает, что это не так, и, таким образом, Generics инвариантны.
Но когда вы используете extends вместе с параметром Type, тогда это позволяет присваиваниено тогда вы должны помнить PECS (Producer Extends и Consumer Super) .
В вашем первом случае у вас есть Type Parameter, объявленный на уровне класса.Теперь, когда вы создаете экземпляр класса LivingThing, вы должны упомянуть параметр типа, такой как LivingThing<LivingThingTypes> livingList = new LivingThing();
.И это становится значением T. Теперь компилятор будет ожидать, что вы отправите тот же самый тип внутри метода create также по причине, которую я упомянул в предыдущем параграфе.
Во втором случае вы создаете новый параметр Type, который привязан только к методу, и здесь вы говорите ему, что V extends LivingThingType
и, таким образом, теперь Class<V>
аргумент типа метода create может принимать Human.class/Hen.class/Cat.class
,Вы можете использовать тот же параметр типа T также вместо V, но во избежание путаницы вы должны называть их иначе, чем вы.