← Назад к вопросам

В чем разница между наследованием и полиморфизмом?

2.0 Middle🔥 111 комментариев
#ООП

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между наследованием и полиморфизмом

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

Наследование: «Что есть общего»

Наследование — это механизм переиспользования кода через создание иерархии классов. Дочерний класс наследует атрибуты и методы родительского.

// Родительский класс (суперкласс)
public class Vehicle {
    protected String brand;
    protected int year;
    
    public Vehicle(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }
    
    public void displayInfo() {
        System.out.println(brand + " (" + year + ")");
    }
}

// Дочерний класс (подкласс) — наследует от Vehicle
public class Car extends Vehicle {
    private int doors;
    
    public Car(String brand, int year, int doors) {
        super(brand, year);
        this.doors = doors;
    }
    
    public int getDoors() {
        return doors;
    }
}

public class Motorcycle extends Vehicle {
    private boolean hasSidecar;
    
    public Motorcycle(String brand, int year, boolean hasSidecar) {
        super(brand, year);
        this.hasSidecar = hasSidecar;
    }
}

// Использование
Car car = new Car("BMW", 2023, 4);
Motorcycle moto = new Motorcycle("Harley", 2022, false);

car.displayInfo();      // Унаследованный метод: BMW (2023)
moto.displayInfo();     // Унаследованный метод: Harley (2022)

Суть наследования:

  • Переиспользование кода (brand, year, displayInfo)
  • Установление отношения "is-a" (Car IS-A Vehicle)
  • Модель иерархии классов
  • Экономия кода (DRY принцип)

Полиморфизм: «Разные поведения, один интерфейс»

Полиморфизм — это способность объектов разных типов отвечать одинаковым вызовам по-разному. Это о поведении, а не о коде.

public class Vehicle {
    // Метод, который будет переопределён
    public void drive() {
        System.out.println("Vehicle is driving");
    }
}

public class Car extends Vehicle {
    @Override
    public void drive() {
        System.out.println("Car is driving on roads");
    }
}

public class Boat extends Vehicle {
    @Override
    public void drive() {
        System.out.println("Boat is driving on water");
    }
}

// ПОЛИМОРФИЗМ В ДЕЙСТВИИ
public static void startDriving(Vehicle vehicle) {
    vehicle.drive(); // Какой drive() вызовется?
    // Зависит от того, какой именно объект
}

// Использование
Vehicle myCar = new Car();
Vehicle myBoat = new Boat();

startDriving(myCar);   // Car is driving on roads
startDriving(myBoat);  // Boat is driving on water

// Одна переменная типа Vehicle
// Разные поведения в зависимости от реального типа!

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

  • Одна переменная, разные реальные типы
  • Разное поведение для одного вызова
  • Связывание методов в runtime (динамический dispatch)
  • Гибкость и расширяемость

Визуальное сравнение

НАСЛЕДОВАНИЕ (IS-A)           ПОЛИМОРФИЗМ
┌─────────────────┐            ┌──────────────────┐
│    Vehicle      │            │  startDriving()  │
│   [brand, drive]│            │  接收 Vehicle    │
└────────┬────────┘            └──────────────────┘
         │                              │
    ┌────┴────┐                    ┌────┴────┐
    │          │                    │          │
  Car      Boat              Car.drive()  Boat.drive()
 [doors]  [watertype]        (roads)      (water)

Наследование — структура    Полиморфизм — поведение

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

1. Переопределение методов (Overriding)

// Наследование + полиморфизм
public abstract class Shape {
    abstract double getArea();  // Абстрактный метод
}

public class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    double getArea() {  // Переопределение
        return Math.PI * radius * radius;
    }
}

public class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    double getArea() {  // Переопределение
        return width * height;
    }
}

// Полиморфный код
List<Shape> shapes = List.of(
    new Circle(5),
    new Rectangle(4, 6)
);

double totalArea = 0;
for (Shape shape : shapes) {
    totalArea += shape.getArea();  // Разные методы!
}

// Shape: Circle.getArea() → 78.54
// Shape: Rectangle.getArea() → 24
// totalArea = 102.54

2. Перегрузка методов (Overloading)

public class Calculator {
    // Перегрузка — разные сигнатуры
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public String add(String a, String b) {
        return a + b;
    }
}

// Использование
Calculator calc = new Calculator();
System.out.println(calc.add(5, 3));           // 8 (int)
System.out.println(calc.add(5.5, 3.2));      // 8.7 (double)
System.out.println(calc.add("Hello", " World")); // Hello World (String)

Важное различие:

  • Overriding — динамическое (runtime): какой метод вызвать зависит от реального типа
  • Overloading — статическое (compile-time): какой метод вызвать видно по типам аргументов

3. Интерфейсы и полиморфизм

// Интерфейс — контракт без наследования структуры
public interface PaymentProcessor {
    void processPayment(double amount);
}

public class CreditCardProcessor implements PaymentProcessor {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing credit card: $" + amount);
    }
}

public class PayPalProcessor implements PaymentProcessor {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing PayPal: $" + amount);
    }
}

public class Order {
    private PaymentProcessor processor;
    
    public Order(PaymentProcessor processor) {
        this.processor = processor;  // Зависит от интерфейса
    }
    
    public void pay(double amount) {
        processor.processPayment(amount); // Полиморфизм через интерфейс!
    }
}

// Использование
Order order1 = new Order(new CreditCardProcessor());
Order order2 = new Order(new PayPalProcessor());

order1.pay(100); // Processing credit card: $100
order2.pay(100); // Processing PayPal: $100

Сравнительная таблица

АспектНаследованиеПолиморфизм
Что этоМеханизм переиспользования кодаМеханизм разных поведений
ЦельЭкономия кода (DRY)Гибкость и расширяемость
СтруктураИерархия классов (tree)Полиморфное поведение
Ключевое словоextends / implements@Override / интерфейсы
РеализацияНаследуем коды и поляПереопределяем методы
ПримерCar extends Vehiclevehicle.drive() вызывает разные методы
Вопрос«Какой код использовать?»«Какой метод вызвать?»
СвязываниеВремя проектированияRuntime

Практический пример: вместе

// НАСЛЕДОВАНИЕ: структурная иерархия
public abstract class Animal {
    protected String name;  // Общее поле
    
    public Animal(String name) {
        this.name = name;
    }
    
    abstract void makeSound();  // Общий контракт
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
    
    @Override
    void makeSound() {
        System.out.println(name + ": Woof!");
    }
}

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
    
    @Override
    void makeSound() {
        System.out.println(name + ": Meow!");
    }
}

// ПОЛИМОРФИЗМ: разные поведения через общий интерфейс
public static void makeAllAnimalsSoundOff(List<Animal> animals) {
    for (Animal animal : animals) {
        animal.makeSound();  // Полиморфный вызов!
    }
}

// Использование
List<Animal> animals = List.of(
    new Dog("Rex"),
    new Cat("Whiskers")
);

makeAllAnimalsSoundOff(animals);
// Rex: Woof!
// Whiskers: Meow!

Итоговый ответ

Наследование отвечает на вопрос: "Как избежать дублирования кода?"

  • Механизм переиспользования
  • Структурная иерархия (IS-A relation)
  • Дочерний класс получает все методы и поля родителя

Полиморфизм отвечает на вопрос: "Как писать гибкий, расширяемый код?"

  • Механизм разных поведений через общий интерфейс
  • Одна переменная, разные реальные типы
  • Runtime динамический dispatch

Вместе они создают мощную систему: наследование даёт структуру, полиморфизм даёт гибкость.