Написать эту статью меня побудило 2 фактора: статья «Когда я говорил», берущая за душу, и опыт проведения собесодований, леденящий душу. К сожалению, даже для людей со значительным опытом в разработке, вопрос «чем отличается абстрактный класс от интерфейса?» может стать поводом для несвязной речи и покраснения лица. Чтобы дать возможность людям избежать «этого неловкого момента» и строить грамотную архитектуру приложения (ниже будет пояснено, почему осведомленность в этом вопросе может повлиять на вашу арихитектуру) я решил осветить эту тему.
Сводная таблица
Я постараюсь донести знания структурированно, сведя все различия в таблицу. Потом мы рассмотрим что-то более подробно.
Критерий сравнения | Абстрактный класс | Интерфейс |
---|---|---|
Наследование | Любой класс может наследовать только один абстрактный класс. | Любой класс может реализовывать (имплементировать, наследовать) множество интерфейсов. |
Модификаторы доступа методов | К неабстактным членам класса применимы любые модификаторы. НО! Абстрактные методы (имеющие модификатор abstract) могут иметь модификатор либо public, либо protected. То есть абстрактные методы не могут быть приватными (ну да, а смысл?). | Методы в интерфейсе могут иметь модификаторы только public и abstract. По умолчанию они уже public abstract. |
Данные | Абстрактный класса может содержать любые поля: статические и экземплярные, константы, private/protected/public. | Интерфейс может содержать только общедоступные константы (public final static int NOT_PI_CONST = -1); |
Наличие реализации | Абстактный класс допускает реализацию методов. | Интерфейс не может содержать никакой реализации методов. |
Возможность описать конструктор | В абстрактом классе можно описать конструктор (или несколько конструкторов). | В интерфейсе нельзя описать конструктор. |
Что выбрать?
В силу отсутствия множественного наследования, к выбору между интерфейсом и абстрактным классом нужно относиться ответсвенно. Но это лишь одна из причин. Используя абстракный класс вы можете сделать вызов метода предка super и получать доступ к его protected полям, что может привести к нарушению инкапсуляции. Поэтому часто рекомендуют использовать вместо наследования композицию или делегирование. Но это рекомендации.
Таким образом, если классы действительно должны находиться в одной иерархической ветви, то есть разделюят общую функциональность и данные, а так же связаны логически по типу «is a» — абстрактный класс уместен. Если же из общего у классов только интерфейс, то «это как бы намекает», что тут уместен интерфейс.
Заключение
Надеюсь, описанные мной разлиция и рекомендации, в сочетании с достаточным временем на проектирование позволят вам улучшишь архитектуру своих приложений. Или ответить на дополнительный вопрос собеседования, помимо ответа о «круглых люках»