Полиморфизм - единый интерфейс для разных реализаций
Разработчику
Аналитику
Тестировщику
Архитектору
Инженеру
Полиморфизм
Что такое полиморфизм?
★ Полиморфизм — способность одного и того же интерфейса (имени операции, типа переменной) работать с разными реализациями. В ООП чаще всего имеют в виду полиморфизм подтипов — объекты разных классов с общим предком обрабатываются через ссылку на базовый тип, а вызывается метод фактического класса (динамическое связывание, virtual / override).
Слово происходит от греческого "много форм". В теории типов различают несколько видов:
| Вид | В ООП | Пример |
|---|---|---|
| Подтипы (включение) | переопределение, интерфейсы | Фигура f = new Круг(); f.Нарисовать() |
| Ad hoc | перегрузка методов | сложить(int,int) и сложить(String,String) |
| Параметрический | обобщения (generics) | List<T> в Java и C# |
В сообществе ООП слово "полиморфизм" обычно означает подтипы и наследование; параметрический полиморфизм называют обобщённым программированием — теория и примеры в Обобщения и обобщённое программирование; синтаксис — generics в C#, типы в Java.
Виртуальный полиморфизм — при динамическом связывании указатель или ссылка базового типа может указывать на объект производного класса; вызов метода разрешается по реальному типу объекта, а не по типу переменной.
Позднее связывание
В учебниках по C++ это называют поздним связыванием (late binding): сигнатуры методов наследуются от предка, а реализация выбирается во время выполнения после переопределения (override, virtual). Код, написанный против базового типа или интерфейса, работает с любым подтипом, подставленным в рантайме.
Чисто виртуальные (абстрактные) методы в базовом классе задают контракт без реализации; конкретику дают только потомки. Множественное наследование (несколько базовых классов у одного потомка) усиливает выразительность, но порождает ромбовидную иерархию, когда два "ветвя" наследуют одного предка — какую переопределённую версию метода взять, становится неоднозначным. В C++ это смягчают виртуальным наследованием; в Java — запретом множественного наследования классов (остаются интерфейсы). См. ООП в C++, композицию вместо глубоких иерархий.

Play ITЗагрузка интерактивного демо…
ИНТЕРФЕЙС Фигура
метод Нарисовать()
КОНЕЦ
КЛАСС Круг РЕАЛИЗУЕТ Фигура
метод Нарисовать() вывести("Круг")
КОНЕЦ
КЛАСС Прямоугольник РЕАЛИЗУЕТ Фигура
метод Нарисовать() вывести("Прямоугольник")
КОНЕЦ
список := [новый Круг(), новый Прямоугольник()]
для каждой ф в список
ф.Нарисовать() // одна операция — разные результаты
конец
Пример:
Код ITЗагрузка примера кода…
Здесь метод нарисовать() имеет разные реализации в зависимости от типа объекта - Круг или Прямоугольник, однако вызывается через единый интерфейс.
Переопределение
★ Переопределение методов — это механизм, при котором подкласс предоставляет свою собственную реализацию метода, унаследованного от родительского класса. Это ключевой аспект полиморфизма. Пример:
Код ITЗагрузка примера кода…
Здесь метод издатьЗвук() переопределяется в методах Кот и Собака. Оба класса наследуются от базового класса Животное, но при вызове метода через Животное будет реализация, соответствующая фактическому типу объекта.
К примеру, "Собака.издатьЗвук()" будет "Гав!", а "Кот.издатьЗвук()" будет "Мяу!".
Метод в базовом классе один и тот же, но его внутренности для каждого подкласса - разные.
Перегрузка
★ Перегрузка методов — это создание нескольких методов с одинаковым именем, но разными параметрами (типами или количеством). Это не связано напрямую с наследованием, в отличие от переопределения, но также является частью полиморфизма.
Пример:
Код ITЗагрузка примера кода…
Здесь в одном классе Математика есть две реализации одного и того же метода - "сложить" - одна для чисел, вторая для строк. Выбор реализации будет зависеть от переданных аргументов.
Когда мы вызовем "сложить(1, 2)" - тип данных определится как целое число (int) и соответственно будет вызвана реализация первая - для чисел. Если же мы напишем вместо целых чисел строки, то будет вывод путём конкатенации, а не суммирования.
Давайте подведём итоги по ООП.
Мы будем повторно изучать особенности ООП для каждого языка, но суть должны были понять корректно. Сведём всё в итог.
| Принцип | Определение | Цель | Механизмы |
|---|---|---|---|
| Абстракция | Выделение важных характеристик объекта и игнорирование несущественных деталей. | Упрощение моделирования сложных систем, фокус на "что" делает объект, а не "как". | Абстрактные классы; Интерфейсы; Скрытие деталей реализации. |
| Инкапсуляция | Объединение данных (атрибутов) и методов работы с ними в единый объект. | Защита данных, управление доступом, обеспечение целостности объекта. | Модификаторы доступа; Геттеры и сеттеры; Автоматические свойства. |
| Наследование | Создание нового класса (подкласса) на основе существующего (родительского), перенимая его свойства и методы. | Уменьшение дублирования кода, повторное использование, расширение функциональности. | Базовые и производные классы; Абстрактные классы; Интерфейсы. |
| Полиморфизм | Способность объекта принимать разные формы в зависимости от контекста. | Гибкость кода, единый интерфейс для работы с разными типами объектов. | Переопределение методов; Перегрузка методов; Интерфейсы; Абстрактные классы. |
Как можно заметить, особенно от всех выделяется именно инкапсуляция, тогда как абстракция, наследование и полиморфизм настолько тесно связаны, что их легко перепутать - они используют одни и те же механизмы интерфейсов, абстрактных классов. Поэтому ООП — это единая парадигма, в которой важны все эти четыре принципа.