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

Когда последний раз писал абстрактный класс?

2.0 Middle🔥 161 комментариев
#Основы Java

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

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

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

Использование абстрактных классов в практике

Это хороший вопрос на собеседовании — показывает, понимает ли кандидат разницу между абстрактными классами и интерфейсами, и когда применять каждый из них.

Когда писать абстрактный класс

1. Иерархия классов с общей реализацией

Абстрактный класс используется когда нужно предоставить общую реализацию для подклассов:

// Пример: система обработки платежей
public abstract class PaymentProcessor {
    // Общая логика для всех процессоров
    protected String merchantId;
    protected String apiKey;
    
    // Абстрактный метод — подклассы должны реализовать
    public abstract TransactionResult process(Payment payment);
    
    // Общая реализация логирования
    public void logTransaction(Payment payment, TransactionResult result) {
        System.out.println("Transaction: " + payment.getId() + 
                         " Status: " + result.getStatus());
    }
    
    // Общая реализация валидации
    protected void validatePayment(Payment payment) {
        if (payment.getAmount() <= 0) {
            throw new IllegalArgumentException("Invalid amount");
        }
    }
}

public class StripeProcessor extends PaymentProcessor {
    @Override
    public TransactionResult process(Payment payment) {
        validatePayment(payment);
        // Специфичная для Stripe логика
        TransactionResult result = callStripeAPI(payment);
        logTransaction(payment, result);
        return result;
    }
}

public class PayPalProcessor extends PaymentProcessor {
    @Override
    public TransactionResult process(Payment payment) {
        validatePayment(payment);
        // Специфичная для PayPal логика
        TransactionResult result = callPayPalAPI(payment);
        logTransaction(payment, result);
        return result;
    }
}

2. Состояние (protected поля)

Абстрактные классы могут иметь состояние с защищенным доступом:

public abstract class DatabaseRepository<T> {
    // Защищённое состояние, доступное подклассам
    protected DataSource dataSource;
    protected String tableName;
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    
    public DatabaseRepository(DataSource dataSource, String tableName) {
        this.dataSource = dataSource;
        this.tableName = tableName;
    }
    
    // Общая логика подключения
    protected Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    
    // Абстрактный метод
    public abstract T findById(Long id);
    
    // Общая логика логирования
    protected void log(String message) {
        logger.info("[" + tableName + "] " + message);
    }
}

public class UserRepository extends DatabaseRepository<User> {
    public UserRepository(DataSource dataSource) {
        super(dataSource, "users");
    }
    
    @Override
    public User findById(Long id) {
        log("Finding user with id: " + id);  // Используем protected метод
        // Реализация с использованием protected dataSource и tableName
        return null;
    }
}

3. Контроль видимости методов

Абстрактный класс позволяет определить protected и private методы, недоступные через интерфейс:

public abstract class OrderProcessor {
    // Публичный контракт
    public abstract void processOrder(Order order);
    
    // Protected вспомогательный метод для подклассов
    protected void saveOrder(Order order) {
        // Реализация
    }
    
    // Приватный метод — полностью скрыт
    private void notifyWarehouse(Order order) {
        // Реализация
    }
}

public class StandardOrderProcessor extends OrderProcessor {
    @Override
    public void processOrder(Order order) {
        // Может использовать protected метод
        saveOrder(order);
        // Не может использовать private notifyWarehouse()
    }
}

// Через интерфейс нельзя выразить такие отношения доступа
public interface OrderProcessor {
    void processOrder(Order order);  // Всё остальное недоступно
}

4. Неабстрактные методы (конкретные реализации)

Абстрактный класс может содержать полностью реализованные методы:

public abstract class HttpClient {
    protected abstract String makeRequest(String url);
    
    // Конкретный метод — реализация готова
    public String get(String url) {
        String response = makeRequest(url);
        if (response == null) {
            throw new RuntimeException("Response is null");
        }
        return response;
    }
    
    public String getJson(String url) {
        return get(url);  // Переиспользуем логику
    }
}

public class RestClient extends HttpClient {
    @Override
    protected String makeRequest(String url) {
        // Специфичная реализация для REST
        return null;
    }
    
    // Наследуем готовые методы get() и getJson()
}

Недавный пример в реальном проекте

// Система обработки документов
public abstract class DocumentProcessor {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    public final ProcessingResult process(Document document) {
        // Template Method Pattern
        validateDocument(document);
        ProcessingResult result = processDocument(document);
        saveResult(document, result);
        return result;
    }
    
    // Абстрактный метод — подклассы реализуют
    protected abstract ProcessingResult processDocument(Document document);
    
    // Общая валидация
    protected void validateDocument(Document document) {
        if (document == null || document.getContent().isEmpty()) {
            throw new IllegalArgumentException("Invalid document");
        }
    }
    
    // Общее сохранение
    protected void saveResult(Document document, ProcessingResult result) {
        logger.info("Processed document: " + document.getId());
    }
}

public class PDFProcessor extends DocumentProcessor {
    @Override
    protected ProcessingResult processDocument(Document document) {
        // Специфичная для PDF логика
        return new ProcessingResult();
    }
}

public class ImageProcessor extends DocumentProcessor {
    @Override
    protected ProcessingResult processDocument(Document document) {
        // Специфичная для изображений логика
        return new ProcessingResult();
    }
}

Абстрактный класс vs Интерфейс

КритерийАбстрактный классИнтерфейс
СостояниеМожет иметь protected поляТолько констант (Java 8+)
Конкретные методыДа, с любой логикойДа, но default методы (Java 8+)
НаследованиеОдин классМножественное
Когда использоватьИерархия с общей реализациейКонтракт / Interface Segregation
Доступprotected, private, publicpublic только

Современный подход

В современной Java часто предпочитают интерфейсы с default методами:

// Вместо абстрактного класса
public interface PaymentProcessor {
    TransactionResult process(Payment payment);
    
    // Default реализация (Java 8+)
    default void logTransaction(Payment payment, TransactionResult result) {
        System.out.println("Transaction: " + payment.getId());
    }
    
    default void validatePayment(Payment payment) {
        if (payment.getAmount() <= 0) {
            throw new IllegalArgumentException("Invalid amount");
        }
    }
}

public class StripeProcessor implements PaymentProcessor {
    @Override
    public TransactionResult process(Payment payment) {
        validatePayment(payment);
        // Реализация
        return null;
    }
}

Таким образом, абстрактные классы используются когда нужна общая реализация, protected состояние, или контроль видимости методов. В современной Java часто достаточно интерфейсов с default методами для большинства сценариев, но абстрактные классы остаются ценным инструментом для построения иерархий классов.