Menu

Абстрактные классы в Java: объяснение ключевого слова abstract

Что такое абстрактный класс в Java, как объявлять абстрактные методы, почему его нельзя инстанцировать и когда выбирать абстрактный класс вместо интерфейса.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Что такое абстрактный класс

Интерфейс объявляет поведение без состояния. Обычный класс полностью реализован, и его можно инстанцировать. Абстрактный класс находится между ними: он может содержать поля, конструкторы и завершённые методы, как обычный класс, но также может оставить некоторые методы нереализованными и запрещает создавать свой экземпляр напрямую. Вы помечаете его ключевым словом abstract.

Идея в том, чтобы собрать в одном месте всё общее для подклассов, при этом заставляя каждый подкласс реализовать те части, которые действительно различаются.

Animal определяет getName() один раз для всех подклассов и объявляет sound() как abstract — метод с сигнатурой, но без тела, который должен предоставить Dog.

Нельзя создать экземпляр абстрактного класса

Поскольку у абстрактного класса могут быть незавершённые методы, его прямое создание оставило бы вас с неполным объектом. Компилятор это отвергает:

Animal a = new Animal("???");   // error: Animal is abstract; cannot be instantiated

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

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

abstract-метод — это обещание, которое подкласс обязан выполнить. Если подкласс забывает реализовать один из них, сам подкласс становится абстрактным, и компилятор сообщает вам об этом. Это главный рычаг абстрактного класса: он гарантирует, что определённое поведение существует, не диктуя, что именно оно делает.

describe() написан в Shape один раз, но при этом вызывает собственный area() каждого подкласса. Абстрактный класс предоставляет общий каркас; подклассы предоставляют конкретику.

Общее состояние и конструкторы

В отличие от традиционного интерфейса, абстрактный класс может содержать поля экземпляра и определять конструкторы. Конструктор сам по себе никогда не создаёт Animal или Shape — он выполняется через super(...) при создании подкласса, инициализируя общее состояние.

Поле balance, методы deposit и applyInterest находятся в одном месте. Абстрактной остаётся только та политика, которая действительно меняется, — interestRate(). Конструктор подкласса обязан вызвать super(...), чтобы инициализировать это унаследованное состояние.

Подводный камень: смешивание abstract и final

abstract и final — противоположности. abstract-метод требует переопределения; final-метод его запрещает. Пометить одно и то же обоими способами — или сделать абстрактный класс final — это ошибка компиляции. Помните также, что у абстрактного класса может быть ноль абстрактных методов: объявить класс abstract только для того, чтобы запретить создание его экземпляров, вполне допустимо и иногда полезно для базовых типов, которые вы хотите только наследовать.

abstract final class Bad { }        // error: abstract and final conflict

abstract class Base {
    abstract final void f();        // error: an abstract method can't be final
}

Абстрактный класс против интерфейса

Они пересекаются, поэтому выбор сводится к тому, что именно вам нужно разделять:

  • Абстрактный класс — используйте его для тесно связанных классов, которые разделяют состояние и код. И Savings, и Checking наследуют Account, получая поле balance и логику deposit. Класс наследует только один.
  • Интерфейс — используйте его для возможности, которую могут разделять несвязанные классы. И Bird, и Airplane могут быть Flyable, не разделяя никакой реализации. Класс может реализовать множество интерфейсов.

Распространённый приём сочетает их: интерфейс задаёт контракт, а абстрактный класс реализует шаблонный код, так что конкретным подклассам остаётся заполнить лишь то, что уникально.

Далее: Полиморфизм

Обратите внимание, что в каждом примере выше мы хранили экземпляр подкласса в переменной абстрактного типа, затем вызывали метод и автоматически получали поведение подкласса. Именно эта способность — один ссылочный тип, множество вариантов поведения во время выполнения — и есть полиморфизм, и именно благодаря ему абстрактные классы и интерфейсы оправдывают себя. Об этом и пойдёт речь на следующей странице.

Часто задаваемые вопросы

Что такое абстрактный класс в Java?

Абстрактный класс — это класс, объявленный с ключевым словом abstract, экземпляр которого нельзя создать напрямую. Он предназначен для наследования. Он может сочетать полностью реализованные методы и поля (общее состояние и код для подклассов) с abstract-методами без тела — их реализация становится обязанностью каждого подкласса.

Можно ли создать экземпляр абстрактного класса в Java?

Нет. new AbstractType() вызывает ошибку компиляции, потому что у абстрактного класса могут быть нереализованные (abstract) методы, и объект получился бы неполным. Вы создаёте экземпляр конкретного подкласса, который реализует все абстрактные методы, а затем сохраняете его в переменной абстрактного типа.

В чём разница между абстрактным классом и интерфейсом в Java?

У абстрактного класса могут быть поля экземпляра, конструкторы и частично реализованная логика, но класс может наследовать только один абстрактный класс. Интерфейс объявляет поведение без состояния экземпляра, и класс может реализовать множество интерфейсов. Используйте абстрактный класс, чтобы разделять состояние и код между тесно связанными подклассами; используйте интерфейс, чтобы дать несвязанным классам общую возможность.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ