Предположим, у меня есть эта иерархия классов ...
public abstract class Animal {
public abstract void eat();
public abstract void talk();
}
class Dog extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
class Cat extends Animal {
@Override
public void eat() {
}
@Override
public void talk() {
}
}
И тогда у меня есть ....
public static <T extends Animal> void addAnimal(T animal) {
animal.eat();
animal.talk();
}
public static void addAnimalPoly(Animal animal) {
animal.eat();
animal.talk();
}
Какая разница при использовании параметров ограниченного типа или полиморфизма?
И когда использовать один или другой?
java
polymorphism
generics
HugoMelo
источник
источник
addAnimals(List<Animal>)
и добавить список кошек!Ответы:
Эти два примера эквивалентны и фактически компилируются в один и тот же байт-код.
Существует два способа добавления ограниченного универсального типа в метод, как в первом примере.
Передача параметра типа другому типу
Эти две сигнатуры метода в конечном итоге совпадают в байтовом коде, но компилятор обеспечивает безопасность типов:
public static <T extends Animal> void addAnimals(Collection<T> animals)
public static void addAnimals(Collection<Animal> animals)
В первом случае допускается только
Collection
(или подтип) ofAnimal
. Во втором случае допускаетсяCollection
(или подтип) с универсальным типомAnimal
или подтипом.Например, в первом методе допускается следующее, но не во втором:
Причина в том, что второй разрешает только коллекции животных, в то время как первый разрешает коллекции любого объекта, который может быть назначен животному (то есть подтипов). Обратите внимание, что если бы этот список был списком животных, в которых, как оказалось, содержалась кошка, любой из методов принял бы его: проблема заключается в общей спецификации коллекции, а не в том, что она на самом деле содержит.
Возврат объектов
В другой раз это имеет значение с возвращением предметов. Предположим, существует следующий метод:
Вы сможете сделать следующее:
Хотя это надуманный пример, в некоторых случаях это имеет смысл. Без обобщений метод должен был бы возвращаться,
Animal
и вам нужно было бы добавить приведение типов, чтобы заставить его работать (это то, что компилятор добавляет к байтовому коду в любом случае за кулисами).источник
Используйте дженерики вместо удручения. «Даункинг» - это плохо, переходя от более общего типа к более конкретному:
... ты веришь, что
a
это кот, но компилятор не может этого гарантировать. Может оказаться собакой во время выполнения.Вот где вы бы использовали дженерики:
Теперь вы можете указать, что хотите охотиться на кошек:
Теперь компилятор может гарантировать, что hunterC будет захватывать только кошек, а hunterD - только собак.
Так что просто используйте обычный полиморфизм, если вы просто хотите обрабатывать определенные классы как их базовый тип. Апкастинг это хорошая вещь. Но если вы попадаете в ситуацию, когда вам нужно обрабатывать определенные классы как их собственный тип, в общем, используйте дженерики.
Или, на самом деле, если вы обнаружите, что вам нужно унизить, используйте дженерики.
РЕДАКТИРОВАТЬ: более общий случай, когда вы хотите отложить решение о том, какие типы обрабатывать. Таким образом, типы становятся параметром, а также значениями.
Скажем, я хочу, чтобы мой класс в зоопарке работал с кошками или губками. У меня нет общего суперкласса. Но я все еще могу использовать:
степень, в которой вы это запираете, зависит от того, что вы пытаетесь сделать;)
источник
Этот вопрос давно устарел, но важный фактор, который следует учитывать, по-видимому, был упущен в отношении того, когда использовать полиморфизм против параметров ограниченного типа. Этот фактор может немного касаться примера, приведенного в вопросе, но, я считаю, очень актуален для более общего «Когда использовать полиморфизм против параметров ограниченного типа?»
TL; DR
Если вы когда-нибудь обнаружите, что переносите код из подкласса в базовый класс вопреки своему здравому мнению, из-за невозможности получить к нему полиморфный доступ, параметры ограниченного типа могут стать потенциальным решением.
Полный ответ
Параметры ограниченного типа могут предоставлять конкретные, не унаследованные методы подкласса для унаследованной переменной-члена. Полиморфизм не может
Чтобы уточнить, расширив свой пример:
Если абстрактный класс AnimalOwner был определен так, чтобы иметь
protected Animal pet;
и выбирать полиморфизм, компилятор выдаст ошибку вpet.scratchBelly();
строке, сообщив вам, что этот метод не определен для Animal.источник
В вашем примере вы не используете (и не должны) использовать ограниченный тип. Используйте параметры ограниченного типа только тогда, когда это необходимо , поскольку они более запутанны для понимания.
Вот некоторая ситуация, когда вы будете использовать параметры ограниченного типа:
Параметры коллекций
тогда вы можете позвонить
zoo.add(dogs)
без компиляции<? extends Animal>
, потому что генерики не являются ковариантными.подклассов
чтобы ограничить тип, который может предоставить подкласс.
Вы также можете использовать несколько границ,
<T extends A1 & A2 & A3>
чтобы гарантировать, что тип является подтипом всех типов в списке.источник