Что такое полиморфизм
Полиморфизм — это один из фундаментальных принципов объектно-ориентированного программирования (ООП), наряду с инкапсуляцией, наследованием и абстракцией. С греческого языка термин «полиморфизм» (πολύμορφος) означает «много форм». В программировании это означает, что один и тот же интерфейс (или имя метода) может иметь разное поведение в зависимости от типа объекта, с которым он используется.
🔹 Суть полиморфизма
Полиморфизм позволяет разрабатывать гибкие и расширяемые программы, в которых один и тот же код может работать с разными типами объектов, не зная их точного класса.
🔸 Виды полиморфизма
- **Компиляторный (или статический, или ранний) полиморфизм
** - **Рантаймовый (или динамический, или поздний) полиморфизм
**
📘 1. Статический (compile-time) полиморфизм
Реализуется через перегрузку методов (method overloading) и перегрузку операторов (operator overloading). Решение о том, какой именно метод вызывать, принимается во время компиляции на основе количества и типа параметров.
🔹 Перегрузка методов (Method Overloading)
Один и тот же метод может иметь несколько версий с разными параметрами.
class Printer {
void print(String s) {
System.out.println(s);
}
void print(int i) {
System.out.println(i);
}
}
Вызов print("Hello") и print(42) вызывает разные реализации.
🔹 Перегрузка операторов (Operator Overloading)
Доступна, например, в C++, но не поддерживается в Java (кроме + для строк).
class Complex {
public:
int real, imag;
Complex(int r, int i) : real(r), imag(i) {}
Complex operator + (const Complex& other) {
return Complex(real + other.real, imag + other.imag);
}
};
📗 2. Динамический (runtime) полиморфизм
Реализуется через наследование и переопределение методов (method overriding). Решение о вызове конкретной версии метода принимается во время выполнения, исходя из реального типа объекта.
🔹 Пример на Java:
class Animal {
void speak() {
System.out.println("Some animal speaks");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void speak() {
System.out.println("Cat meows");
}
}
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.speak(); // Dog barks
a2.speak(); // Cat meows
Хотя a1 и a2 объявлены как Animal, вызывается метод, соответствующий фактическому типу объекта.
🔍 Реализация полиморфизма в разных языках
-
Java, C#, Kotlin, Python — поддерживают динамический полиморфизм через переопределение и интерфейсы.
-
C++ — реализует полиморфизм через виртуальные функции.
-
Python — язык с динамической типизацией, использует "утиный тип" (duck typing): _"Если выглядит как утка и крякает как утка — значит, это утка."
_
class Duck:
def speak(self):
print("Quack!")
class Person:
def speak(self):
print("Hello!")
def make_speak(entity):
entity.speak()
make_speak(Duck()) # Quack!
make_speak(Person()) # Hello!
🧱 Полиморфизм через интерфейсы
Полиморфизм часто реализуется с помощью интерфейсов и абстрактных классов, когда объекты разных типов реализуют один интерфейс и могут использоваться одинаково.
interface Shape {
double area();
}
class Circle implements Shape {
double radius;
Circle(double r) { radius = r; }
public double area() {
return Math.PI \* radius \* radius;
}
}
class Square implements Shape {
double side;
Square(double s) { side = s; }
public double area() {
return side \* side;
}
}
Shape s1 = new Circle(2.0);
Shape s2 = new Square(3.0);
System.out.println(s1.area()); // 12.566...
System.out.println(s2.area()); // 9.0
💡 Преимущества полиморфизма
-
Гибкость: код работает с базовыми типами (интерфейсами), а не с конкретными реализациями.
-
Масштабируемость: легко добавлять новые типы, не изменяя существующий код.
-
Переиспользуемость: обобщение поведения для разных классов.
📎 Полиморфизм в шаблонах проектирования
Полиморфизм лежит в основе многих шаблонов проектирования:
-
Стратегия — использование разных алгоритмов через общий интерфейс.
-
Команда — разные действия, вызываемые одинаково.
-
Фабрика — создание объектов, не указывая их точного класса.
🧠 Пример с коллекциями
Допустим, у вас есть список объектов типа Shape. Вы не знаете, что именно в этом списке — Circle, Rectangle, Triangle — но можете вызывать у всех area():
List<Shape> shapes = Arrays.asList(new Circle(2), new Square(3));
for (Shape s : shapes) {
System.out.println(s.area());
}
🆚 Полиморфизм vs перегрузка
Особенность | Перегрузка (Overloading) | Переопределение (Overriding) |
---|---|---|
Тип полиморфизма | Статический | Динамический |
--- | --- | --- |
Место решения | Во время компиляции | Во время выполнения |
--- | --- | --- |
Аргументы | Разные сигнатуры | Одинаковая сигнатура |
--- | --- | --- |
Применение | В одном классе | В классе-наследнике |
--- | --- | --- |
Полиморфизм — мощный инструмент, позволяющий создавать чистый, расширяемый и легко сопровождаемый код, при этом абстрагируясь от деталей реализации и сосредотачиваясь на интерфейсах и поведении объектов.