Как в ООП реализуется разное поведение одного метода в разных классах?

«Как в ООП реализуется разное поведение одного метода в разных классах?» — вопрос из категории ООП, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Это реализация полиморфизма – одного из ключевых принципов ООП. В Java достигается через:

1. Абстрактные методы и классы

abstract class Shape {
    protected String color;

    // Абстрактный метод – реализация обязательна в наследниках
    public abstract double calculateArea();

    // Конкретный метод – общий для всех наследников
    public String getColor() {
        return color;
    }
}

class Circle extends Shape {
    private double radius;

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;  // Своя реализация
    }
}

class Rectangle extends Shape {
    private double width, height;

    @Override
    public double calculateArea() {
        return width * height;  // Другая реализация
    }
}

2. Переопределение методов (override)

class Animal {
    public void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

class Dog extends Animal {
    @Override  // Аннотация для явного указания переопределения
    public void makeSound() {
        System.out.println("Woof! Woof!");  // Специфичное поведение
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");  // Другое специфичное поведение
    }
}

3. Интерфейсы и их реализации

interface PaymentProcessor {
    // Общий контракт
    boolean processPayment(double amount);
}

class CreditCardProcessor implements PaymentProcessor {
    @Override
    public boolean processPayment(double amount) {
        // Логика обработки кредитной карты
        System.out.println("Processing credit card: $" + amount);
        return true;
    }
}

class PayPalProcessor implements PaymentProcessor {
    @Override
    public boolean processPayment(double amount) {
        // Совершенно другая логика
        System.out.println("Processing PayPal: $" + amount);
        return authorizePayPal(amount);
    }

    private boolean authorizePayPal(double amount) { /* ... */ }
}

4. Использование полиморфизма в коде

public class PaymentService {
    // Метод работает с ЛЮБЫМ PaymentProcessor
    public void executePayment(PaymentProcessor processor, double amount) {
        if (processor.processPayment(amount)) {
            System.out.println("Payment successful");
        }
    }

    public static void main(String[] args) {
        PaymentService service = new PaymentService();

        // Разные реализации, один интерфейс
        service.executePayment(new CreditCardProcessor(), 100.0);
        service.executePayment(new PayPalProcessor(), 200.0);

        // Работа с коллекцией разных животных
        List<Animal> animals = Arrays.asList(new Dog(), new Cat(), new Dog());
        for (Animal animal : animals) {
            animal.makeSound();  // Вызовется ПРАВИЛЬНАЯ реализация для каждого типа
        }
    }
}

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

  • Расширяемость – можно добавлять новые реализации без изменения существующего кода
  • Инкапсуляция – клиентский код зависит от абстракции, а не от конкретных реализаций
  • Упрощение кода – общая логика работы с разными типами объектов