Что такое полиморфизм в объектно-ориентированном программировании?

Ответ

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

Основные формы полиморфизма:

  1. Полиморфизм подтипов (или наследования): Самый распространенный вид. Метод, объявленный в родительском классе или интерфейсе, переопределяется в дочерних классах.

    // Пример на Java
    abstract class Shape {
        abstract double area(); // Общий интерфейс
    }
    
    class Circle extends Shape {
        private double radius;
        Circle(double r) { this.radius = r; }
        @Override
        double area() { return Math.PI * radius * radius; } // Своя реализация
    }
    
    class Square extends Shape {
        private double side;
        Square(double s) { this.side = s; }
        @Override
        double area() { return side * side; } // Другая реализация
    }
    
    // Полиморфное использование:
    List<Shape> shapes = Arrays.asList(new Circle(5), new Square(4));
    for (Shape s : shapes) {
        System.out.println(s.area()); // Вызовется правильный метод для Circle и Square
    }
  2. Параметрический полиморфизм (обобщенное программирование / дженерики): Позволяет создавать типы и методы, которые работают с данными разных типов, оставаясь типобезопасными.

    // Обобщенный (полиморфный) контейнер
    class Box<T> {
        private T content;
        void set(T content) { this.content = content; }
        T get() { return content; }
    }
    // Один и тот же класс Box можно использовать для String, Integer и т.д.
    Box<String> stringBox = new Box<>();
    Box<Integer> intBox = new Box<>();
  3. Ad-hoc полиморфизм (перегрузка методов): Возможность иметь несколько методов с одним именем, но разными типами или количеством параметров. Компилятор выбирает подходящий метод на этапе компиляции.

    class Calculator {
        int add(int a, int b) { return a + b; }
        double add(double a, double b) { return a + b; } // Перегрузка
        String add(String a, String b) { return a + b; } // Еще одна перегрузка
    }

Преимущества полиморфизма:

  • Гибкость и расширяемость кода: Можно добавлять новые типы (например, Triangle), не меняя существующий код, работающий с Shape.
  • Упрощение взаимодействия: Клиентский код зависит от абстракции (Shape), а не от конкретных реализаций (Circle, Square).
  • Повторное использование: Общая логика может быть инкапсулирована в базовом классе или интерфейсе.

Ответ 18+ 🔞

А, полиморфизм! Ну это, блядь, классика, ебать мои старые костыли. Если по-простому, то это когда у тебя куча разных штук прикидываются одной и той же, но внутри делают всё по-своему. Как будто ты звонишь в одну и ту же службу доставки, а тебе привозит то пиццу, то суши, то водку с селёдкой — интерфейс один («заказ»), а реализация разная, ёпта.

Основные приколюхи:

  1. Полиморфизм подтипов (или наследования): Это когда сын обещает отцу, что будет зарабатывать деньги, а потом один идёт в айти, другой торгует с лотка, а третий просто отжимает мелочь у школьников. Обещание одно («зарабатывать»), а способы — овердохуища разные.

    // Пример на Java
    abstract class Shape {
        abstract double area(); // Все клянутся, что умеют считать площадь
    }
    
    class Circle extends Shape {
        private double radius;
        Circle(double r) { this.radius = r; }
        @Override
        double area() { return Math.PI * radius * radius; } // Считает по-своему, через пи-ар-квадрат
    }
    
    class Square extends Shape {
        private double side;
        Square(double s) { this.side = s; }
        @Override
        double area() { return side * side; } // А этот тупо сторону на сторону множит
    }
    
    // А вот тут магия: ты всех их в один мешок засунул и команду одну даёшь
    List<Shape> shapes = Arrays.asList(new Circle(5), new Square(4));
    for (Shape s : shapes) {
        System.out.println(s.area()); // И каждый, сука, сам разберётся, как ему считать!
    }
  2. Параметрический полиморфизм (дженерики): Это как коробка, в которую можно засунуть что угодно: хоть носки, хоть золотые слитки, хоть дохлую кошку. Коробка одна, а содержимое — хуй с горы, какое угодно.

    // Универсальная коробка, она же — манда с ушами
    class Box<T> {
        private T content;
        void set(T content) { this.content = content; }
        T get() { return content; }
    }
    // И вот ты ей пользуешься:
    Box<String> stringBox = new Box<>(); // Суёшь строки
    Box<Integer> intBox = new Box<>(); // А тут уже циферки
    // Главное — не перепутать, а то будет тебе хиросима и нигерсраки.
  3. Ad-hoc полиморфизм (перегрузка методов): Это когда у тебя один чувак, но он может делать одно и то же по-разному, смотря что ему дать. Попросишь сложить два числа — сложит, попросишь склеить две строки — склеит. Умный, блядь, как мартышлюшка с калькулятором.

    class Calculator {
        int add(int a, int b) { return a + b; } // Для целых
        double add(double a, double b) { return a + b; } // Для дробных
        String add(String a, String b) { return a + b; } // А это уже для текста, конкатенация, ёклмн
    }
    // Компилятор сам догадается, какой метод вызвать, по тому, какой аргумент ты суёшь.

Нахуя это всё нужно?

  • Гибкость, ёпта: Захотел добавить новый тип Triangle — добавил, и весь старый код, который работает с Shape, даже бздеть не начнёт, он будет и с треугольником работать.
  • Не паришь мозги: Ты пишешь код не для каждого типа отдельно, а для общей абстракции. Как будто говоришь «принеси напиток», а тебе несут то пиво, то чай, то бензин — тебе похуй, главное, что жидкость.
  • Не изобретаешь велосипед: Всю общую логику можно запихнуть в базовый класс, а детали уже допиливают наследники. Красота, ядрёна вошь!

Видео-ответы