Полиморфизм против наследования
Предположим, что у меня есть два класса: Animal and Dog. Собака является подклассом Animal. Я делаю следующий код:
Animal a = new Dog();
Теперь я могу вызывать методы класса Dog через переменную.
Но мой вопрос заключается в следующем: если я могу вызвать все методы Animal через объекты Dog (наследование), чем использовать принцип полиморфизма? Я могу просто объявить:
Dog d = new Dog();
В этом объявлении можно использовать все методы Animal и Dog. Так зачем использовать полиморфизм? Большое спасибо за ваш ответ.
Ответы
Ответ 1
В Java понятия полиморфизма и наследования "свариваются"; в общем, это не должно быть так:
- Полиморфизм позволяет вам вызывать методы класса, не зная точного типа класса
- Наследование позволяет производным классам совместно использовать интерфейсы и код их базовых классов
Существуют языки, где наследование отделяется от полиморфизма:
- В С++ вы можете наследовать класс без создания полиморфного поведения (т.е. не помечать функции в базовом классе с помощью
virtual
)
- В Objective C вы можете реализовать метод для несвязанного класса и вызвать его из места, которое знает только сигнатуру метода.
Возвращаясь к Java, причина использования полиморфизма - это развязка вашего кода от деталей реализации его сторонних сторон: например, если вы можете написать метод Feed(Animal animal)
, который работает для всех видов животных, метод будет оставаться применимым, если вы добавите дополнительные подклассы или реализации Animal
. Это контрастирует с методом Feed(Dog dog)
, который будет тесно связан с собаками.
Что касается
Dog d = new Dog();
не существует общей причины, чтобы избежать этого, если вы знаете, что остальная часть вашего метода касается конкретно с собаками. Однако во многих случаях это не так: например, ваш класс или ваши методы часто нечувствительны к конкретной реализации, например
List<Integer> numbers = new ArrayList<Integer>();
В подобных случаях вы можете заменить new ArrayList<Integer>()
на new LinkedList<Integer>()
и знать, что ваш код будет компилироваться. Напротив, если ваш список numbers
был объявлен как ArrayList<Integer> numbers
, такое переключение может быть не уверенным.
Это называется "программирование для интерфейса". Существует очень хороший ответ в разделе "Переполнение стека", объясняющее его.
Ответ 2
У вас могут быть другие реализации класса Animal
, например Cat
. Тогда вы можете сказать
Animal a = new Dog();
Animal b = new Cat();
Вы можете вызвать методы класса Animal
, не заботясь о том, какая реализация на самом деле есть, а polymorphism вызовет правильный метод. Например.
a.speak(); // "Woof"
b.speak(); // "Meow"
Действительно, это не "Полиморфизм против наследования", а "Полиморфизм с использованием наследования".
Ответ 3
Полиморфизм позволяет вам написать метод, который работает для любого Animal
:
public void pet(Animal animal) {
...
}
Этот метод принимает Dog
, Cat
и т.д., включая подклассы Animal
, которые еще не записаны.
Если бы метод принимал Dog
, это не сработало бы для Cat
и т.д.
Ответ 4
Если вы уверены, что всегда будет собакой, для этого нет причин. Вы можете использовать Dog d = new Dog(); как вы описали. Но скажем, вы использовали метод вместо конструктора. Метод вернул животное, и вы не знаете, какую реализацию животного вы получите. Вы все равно сможете использовать те же методы для животного (даже если это собака, кошка-слон и т.д.).
Для целей расширяемости наследование упрощает вещи. Когда вы хотите создать слона или кошку, которые также используют некоторые методы для животных, вы можете легко получить их, имея животное как суперкласс.
Ответ 5
Обычно заданный вами вопрос больше похож на Inheritance vs Composition:). Еще один пример "реальной жизни", почему полезно использовать полиморфизм, например, использование шаблона проектирования стратегии. У вас может быть много реализаций TaxPolicy: UsaTaxPolicy, CanadaTaxPolicy, EuTaxPolicy и т.д. Если у вас есть метод calculateFinalPrice, который также должен рассчитать налог, то вы вводите правильную реализацию, и выполняется хороший расчет, независимо от того, вы прошли через Usa, Canada или Eu.
Ответ 6
Наследование - это динамический полиморфизм. Я имею в виду, что когда вы удаляете наследование, вы больше не можете переопределять.