Что такое полиморфизм


Полиморфизм — это один из фундаментальных принципов объектно-ориентированного программирования (ООП), наряду с инкапсуляцией, наследованием и абстракцией. С греческого языка термин «полиморфизм» (πολύμορφος) означает «много форм». В программировании это означает, что один и тот же интерфейс (или имя метода) может иметь разное поведение в зависимости от типа объекта, с которым он используется.

🔹 Суть полиморфизма

Полиморфизм позволяет разрабатывать гибкие и расширяемые программы, в которых один и тот же код может работать с разными типами объектов, не зная их точного класса.

🔸 Виды полиморфизма

  1. **Компиляторный (или статический, или ранний) полиморфизм
    **
  2. **Рантаймовый (или динамический, или поздний) полиморфизм
    **

📘 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)
Тип полиморфизма Статический Динамический
--- --- ---
Место решения Во время компиляции Во время выполнения
--- --- ---
Аргументы Разные сигнатуры Одинаковая сигнатура
--- --- ---
Применение В одном классе В классе-наследнике
--- --- ---

Полиморфизм — мощный инструмент, позволяющий создавать чистый, расширяемый и легко сопровождаемый код, при этом абстрагируясь от деталей реализации и сосредотачиваясь на интерфейсах и поведении объектов.