В чем разница между наследованием и полиморфизмом?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между наследованием и полиморфизмом
Наследование и полиморфизм — это два разных механизма ООП, хотя они часто работают вместе. Часто разработчики путают их, считая одно частью другого. На самом деле это независимые концепции.
Наследование: «Что есть общего»
Наследование — это механизм переиспользования кода через создание иерархии классов. Дочерний класс наследует атрибуты и методы родительского.
// Родительский класс (суперкласс)
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 Vehicle | vehicle.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
Вместе они создают мощную систему: наследование даёт структуру, полиморфизм даёт гибкость.