Абстрактные классы
Определение
Абстрактный класс нельзя создать напрямую, но он может хранить состояние, иметь конструкторы и объединять общее поведение семейства подтипов.
abstract class Importer {
private final Clock clock;
protected Importer(Clock clock) {
this.clock = clock;
}
public final Result run(Input input) {
validate(input);
Data data = parse(input);
return new Result(data, clock.instant());
}
protected void validate(Input input) {
Objects.requireNonNull(input);
}
protected abstract Data parse(Input input);
}Абстрактный метод объявляет контракт без тела. Конкретный наследник обязан реализовать его либо сам остаться абстрактным.
Что может содержать abstract class
- поля экземпляра и статические поля;
- конструкторы;
- обычные,
final,staticи abstract-методы; - вложенные типы;
- любой допустимый модификатор доступа для членов.
Абстрактный класс может не иметь абстрактных методов: тогда запрет создания экземпляра сам является частью дизайна.
Абстрактный метод не может быть private, static или final: его нужно увидеть и переопределить в экземпляре наследника.
Template Method
В примере run фиксирует общий алгоритм, а parse оставляет изменяемый шаг. final защищает порядок операций:
final class CsvImporter extends Importer {
CsvImporter(Clock clock) {
super(clock);
}
@Override
protected Data parse(Input input) {
return parseCsv(input);
}
}Такой шаблон удобен, когда общее состояние и жизненный цикл действительно принадлежат базовому классу. Для независимых стратегий композиция с интерфейсом обычно проще.
Абстрактный класс и интерфейс
| Абстрактный класс | Интерфейс |
|---|---|
| один базовый класс | можно реализовать несколько интерфейсов |
| поля состояния экземпляра | нет обычного состояния экземпляра |
| конструкторы | конструкторов нет |
| любые модификаторы методов | методы контракта публичны; возможны default/static/private helpers |
| общий код и состояние | роль/способность и слабая связь |
Интерфейс тоже может иметь реализацию default, поэтому различие не сводится к фразе «в интерфейсе нет кода».
Ловушки
- Объявлять abstract-метод с телом.
- Пытаться совместить
abstractсfinal,privateилиstatic. - Выносить в базовый класс случайно похожие поля несвязанных сущностей.
- Открывать
protectedmutable-поля вместо защищённых операций. - Использовать наследование там, где нужна заменяемая стратегия.
- Вызывать абстрактный/переопределяемый метод из конструктора.
Вопросы на собеседовании
- Может ли абстрактный класс не содержать абстрактных методов?
- Может ли у него быть конструктор и зачем?
- Какие модификаторы несовместимы с abstract-методом?
- Чем абстрактный класс отличается от современного интерфейса?
- Что такое Template Method?
- Когда лучше выбрать композицию с интерфейсом?
Связанные заметки
Основы ООП · Наследование · Интерфейсы Java · Переопределение Java · Конструкторы Java