Ответ
Абстрактные классы не устарели, но их роль изменилась с появлением интерфейсов с default-методами (Java 8). Выбор зависит от проектируемых отношений.
| Критерий | Абстрактный класс | Интерфейс (с default-методами) |
|---|---|---|
| Наследование | Один класс (единичное) | Множество интерфейсов |
| Состояние (поля) | ✅ Может иметь поля экземпляра с модификаторами доступа | ❌ Только public static final константы |
| Конструктор | ✅ Есть | ❌ Нет |
| Реализация методов | ✅ Полная и абстрактная реализация | ✅ Только default и static методы (Java 8+), иначе абстрактные |
| Цель | ОБЩАЯ ЛОГИКА И СОСТОЯНИЕ для близкородственных объектов. Выражает отношение "IS-A". | ОБЩИЙ КОНТРАКТ для разнородных объектов. Выражает отношение "CAN-DO" (роль, способность). |
Когда использовать абстрактный класс:
- Несколько классов имеют общее состояние и поведение, которое можно вынести в базовую реализацию.
- Нужен не-public конструктор (например, для паттерна Строитель).
- Планируется добавлять protected или package-private методы для наследников.
Когда использовать интерфейс:
- Нужна множественная реализация разных контрактов.
- Классы, реализующие интерфейс, не связаны иерархией наследования (например,
CarиClockмогут реализовыватьAudible). - Определяется API для библиотек или слоев (Service, Repository).
- Работа с функциональными интерфейсами и лямбда-выражениями.
Пример гибридного подхода:
// Интерфейс определяет основную способность
interface Transport {
void move();
default void honk() { System.out.println("Beep!"); } // Дефолтная реализация
}
// Абстрактный класс предоставляет общую логику для наземного транспорта
abstract class GroundTransport implements Transport {
protected int wheels; // Общее состояние
public GroundTransport(int wheels) { this.wheels = wheels; } // Конструктор
@Override
public void move() { System.out.println("Moving on " + wheels + " wheels"); } // Общая реализация
}
// Конкретные классы
class Car extends GroundTransport {
public Car() { super(4); }
}
class Bike extends GroundTransport {
public Bike() { super(2); }
}