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

Какая основная задача полиморфизма?

1.3 Junior🔥 241 комментариев
#ООП

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

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

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

Основная задача полиморфизма

Полиморфизм - один из четырёх столпов объектно-ориентированного программирования (ООП). Его основная задача - писать гибкий, расширяемый код, который работает с объектами разных типов через единый интерфейс.

Слово "полиморфизм" буквально означает "много форм" (poly = много, morph = форма).

Проблема без полиморфизма

// Плохо - без полиморфизма
public class PaymentProcessor {
    public void processPayment(Object payment, double amount) {
        if (payment instanceof CreditCard) {
            CreditCard card = (CreditCard) payment;
            card.chargeCard(amount);
        } else if (payment instanceof PayPal) {
            PayPal paypal = (PayPal) payment;
            paypal.sendMoney(amount);
        } else if (payment instanceof Bitcoin) {
            Bitcoin bitcoin = (Bitcoin) payment;
            bitcoin.transferBitcoin(amount);
        } else if (payment instanceof GooglePay) {
            // ... ещё один условный оператор
        }
        // Каждый раз добавляем новый тип - нужно менять код!
    }
}

Проблемы:

  • Код становится огромным с множеством if-else
  • При добавлении нового способа оплаты нужно менять класс PaymentProcessor
  • Нарушает принцип Open/Closed (открыт для расширения, закрыт для модификации)
  • Сложно тестировать
  • Нарушает SOLID принципы

Решение - полиморфизм

// Интерфейс - единый контракт для всех способов оплаты
public interface PaymentMethod {
    void pay(double amount);
    void refund(double amount);
}

// Разные реализации
public class CreditCard implements PaymentMethod {
    @Override
    public void pay(double amount) {
        System.out.println("Charging credit card: " + amount);
        // Логика для кредитной карты
    }
    
    @Override
    public void refund(double amount) {
        System.out.println("Refunding to credit card: " + amount);
    }
}

public class PayPal implements PaymentMethod {
    @Override
    public void pay(double amount) {
        System.out.println("Sending via PayPal: " + amount);
        // Логика для PayPal
    }
    
    @Override
    public void refund(double amount) {
        System.out.println("Refunding via PayPal: " + amount);
    }
}

public class Bitcoin implements PaymentMethod {
    @Override
    public void pay(double amount) {
        System.out.println("Transferring Bitcoin: " + amount);
        // Логика для Bitcoin
    }
    
    @Override
    public void refund(double amount) {
        System.out.println("Refunding Bitcoin: " + amount);
    }
}

// Отличное! Код работает с любым способом оплаты
public class PaymentProcessor {
    public void processPayment(PaymentMethod payment, double amount) {
        payment.pay(amount);  // Полиморфизм! Неважно, какой именно тип
    }
    
    public void refundPayment(PaymentMethod payment, double amount) {
        payment.refund(amount);
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        PaymentProcessor processor = new PaymentProcessor();
        
        // Один код - бесконечное количество типов
        processor.processPayment(new CreditCard(), 100);
        processor.processPayment(new PayPal(), 200);
        processor.processPayment(new Bitcoin(), 300);
        
        // Добавили новый способ оплаты? Код PaymentProcessor не меняется!
        processor.processPayment(new ApplePay(), 150);
    }
}

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

1. Полиморфизм параметров (Compile-time)

// Перегрузка методов (Method 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();
calc.add(1, 2);           // int версия
calc.add(1.5, 2.5);       // double версия
calc.add("Hello", "World"); // String версия

Когда какой метод вызывается решается в compile-time (во время компиляции).

2. Полиморфизм подтипов (Runtime Polymorphism)

// Переопределение методов (Method Overriding)
public abstract class Animal {
    public abstract void sound();
}

public class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("Woof!");
    }
}

public class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("Meow!");
    }
}

// Использование
public class Zoo {
    public void makeSound(Animal animal) {
        animal.sound();  // JVM решает во время выполнения (runtime)!
    }
}

// Использование
Zoo zoo = new Zoo();
zoo.makeSound(new Dog());  // Выведет: Woof!
zoo.makeSound(new Cat());  // Выведет: Meow!

// Это работает потому что Dog и Cat это подтипы Animal
Animal myPet = new Dog();  // Up-casting (всегда безопасно)
myPet.sound();  // Выведет: Woof!

Когда какой метод вызывается решается в runtime (во время выполнения программы).

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

1. Гибкость кода

// Функция работает с ДЛЮ```БЫМ типом данных
public <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

printArray(new String[]{"Hello", "World"});
printArray(new Integer[]{1, 2, 3});
printArray(new Double[]{1.1, 2.2, 3.3});

2. Расширяемость без изменения кода

// Интерфейс остаётся неизменным
public interface Logger {
    void log(String message);
}

// А реализаций может быть сколько угодно
public class FileLogger implements Logger { ... }
public class DatabaseLogger implements Logger { ... }
public class SlackLogger implements Logger { ... }
public class CloudLogger implements Logger { ... }

// Код, использующий Logger, работает со всеми
public class Application {
    private Logger logger;
    
    public Application(Logger logger) {
        this.logger = logger;  // Зависит от интерфейса, не от конкретной реализации
    }
    
    public void doSomething() {
        logger.log("Doing something");  // Работает с любым Logger!
    }
}

Добавили новый способ логирования? Просто создай новый класс, реализующий Logger. Код Application менять не нужно!

3. Коллекции разных типов

// Одна коллекция может содержать разные типы
List<PaymentMethod> payments = new ArrayList<>();
payments.add(new CreditCard());
payments.add(new PayPal());
payments.add(new Bitcoin());

// Обработка всех одним циклом
for (PaymentMethod payment : payments) {
    payment.pay(100);  // Полиморфизм! Каждый вызовет свой метод
}

4. Слабая связанность (Loose Coupling)

// Плохо - сильная связанность
public class EmailSender {
    public void sendEmail(String to, String message) { ... }
}

public class UserService {
    private EmailSender emailSender = new EmailSender();  // Жёстко зависит!
    
    public void registerUser(User user) {
        // ...
        emailSender.sendEmail(user.getEmail(), "Welcome");
    }
}

// Хорошо - слабая связанность через интерфейс
public interface NotificationService {
    void notify(String recipient, String message);
}

public class EmailNotification implements NotificationService { ... }
public class SMSNotification implements NotificationService { ... }
public class SlackNotification implements NotificationService { ... }

public class UserService {
    private NotificationService notificationService;
    
    public UserService(NotificationService notificationService) {
        this.notificationService = notificationService;  // Зависит от интерфейса!
    }
    
    public void registerUser(User user) {
        // ...
        notificationService.notify(user.getEmail(), "Welcome");
    }
}

// Использование
UserService service1 = new UserService(new EmailNotification());
UserService service2 = new UserService(new SMSNotification());
UserService service3 = new UserService(new SlackNotification());
// Код не меняется - меняется только способ отправки!

Практические примеры из Spring

// Spring Repository Pattern - классический пример полиморфизма
public interface UserRepository {
    User findById(Long id);
    void save(User user);
    void delete(Long id);
}

public class JdbcUserRepository implements UserRepository { ... }
public class JpaUserRepository implements UserRepository { ... }
public class MongoUserRepository implements UserRepository { ... }

// UserService не знает и не заботится, какая реализация используется
public class UserService {
    private UserRepository repository;  // Может быть любая реализация!
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
    
    public User getUser(Long id) {
        return repository.findById(id);
    }
}

Главные выводы

Основная задача полиморфизма:

  1. Гибкость - один код работает с разными типами
  2. Расширяемость - добавляйте новые типы без изменения старого кода
  3. Слабая связанность - классы зависят от интерфейсов, а не от конкретных реализаций
  4. Переиспользуемость - код, написанный для интерфейса, работает для всех реализаций
  5. Тестируемость - легко подменять реальные объекты на mock-ы

Правило: пиши код, зависящий от интерфейсов и абстрактных классов, а не от конкретных реализаций. Это сердце SOLID принципов и хорошего ОО дизайна.

Какая основная задача полиморфизма? | PrepBro