Каковы отличия между абстрактным классом и интерфейсом?
Короткое различие.
Абстрактный класс — это класс, у которого не реализован один или больше методов (некоторые языки требуют такие методы помечать специальными ключевыми словами).
Интерфейс — это абстрактный класс, у которого ни один метод не реализован, все они публичные и нет переменных класса.
Интерфейс нужен обычно когда описывается только интерфейс (тавтология). Например, один класс хочет дать другому возможность доступа к некоторым своим методам, но не хочет себя «раскрывать». Поэтому он просто реализует интерфейс.
Абстрактный класс нужен, когда нужно семейство классов, у которых есть много общего. Конечно, можно применить и интерфейс, но тогда нужно будет писать много идентичного кода.
В некоторых языках (С++) специального ключевого слова для обозначения интерфейсов нет.
Можно считать, что любой интерфейс — это уже абстрактный класс, но не наоборот.
tl;dr: Абстрактный класс — средство разработки классов на нижнем уровне, средство для повторного использования кода; интерфейс — средство выражения семантики класса. Таким образом, это совершенно разные, мало связанные между собой понятия.
Думайте об этом по-другому.
Абстрактный класс — это «заготовка» класса: реализовано большинство методов (включая внутренние), кроме нескольких. Эти несколько нереализованных методов вполне могут быть внутренними методами класса, они лишь уточняют детали имплементации. Абстрактный класс — средство для повторного использования кода, средство, чтобы указать, какой метод обязан быть перекрыт для завершения написания класса.
Интерфейс же — это своего рода контракт: интерфейсы используются в определениях чтобы указать, что объект, который будет использован на самом деле, должен реализовывать (для входных параметров) или будет гарантированно реализовывать (для выходных параметров) набор методов и (что намного важнее!) иметь определённую семантику. Интерфейс вполне может быть и пустым, тем не менее, имплементировать интерфейс означает поддерживать данную семантику.
Абстрактные классы идеологически схожи с шаблонами C++: и те, и другие являются заготовками классов, но шаблону для получения класса нужно специфицировать шаблонные типы, а абстрактному классу — абстрактные методы.
Интерфейсы идеологически схожи с заголовочными файлами C++: они раскрывают методы и скрывают конкретную реализацию.
Вопрос о том, является ли интерфейс или абстрактный класс собственно классом — техническая подробность реализации, зависящая от конкретного языка программирования. Например, в C++ интерфейсы отсутствуют вовсе, и их приходится эмулировать классами без данных. Абстрактный класс в C++ как таковой также отсутствует, но им можно считать любой класс с абстрактными методами. (Отсюда ограничение C++: как минимум 1 абстрактный метод в абстрактном классе.) Также в C++ можно (непрямо) инстанциировать абстрактный класс, вызвать абстрактный метод и (возможно) получить ошибку времени выполнения. В C# интерфейсы и абстрактные классы встроены в язык.
Пример (на C#, конкретный язык значения не имеет):
// общий код для всех животных
abstract class АбстрактноеЖивотное
{
public int Возраст { get; protected set; }
public int Вес { get; protected set; }
public bool Спит { get; protected set; }
public void ПодатьГолос()
{
if (!Спит && Возраст > ВозрастПрорезанияГолоса)
РеализацияПодатьГолос();
}
abstract protected void РеализацияПодатьГолос();
readonly protected int ВозрастПрорезанияГолоса;
}
class Собака : АбстрактноеЖивотное
{
override protected void РеализацияПодатьГолос()
{
Гав();
}
public void Гав()
{
// реализация
}
public Собака() { ВозрастПрорезанияГолоса = 2; }
}
class Кошка : АбстрактноеЖивотное
{
override protected void РеализацияПодатьГолос()
{
Мяу();
}
public void Мяу()
{
// реализация
}
public Кошка() { ВозрастПрорезанияГолоса = 1; }
}
interface IЖивотное
{
int ИнвентарныйНомер { get; }
}
class Лев : ОбитательЗоопарка, IЖивотное
{
// ...
}
class Зебра : ОбитательЗоопарка, IЖивотное
{
// ...
}
class Сторож : ОбитательЗоопарка
{
}
// ...
void Инвентаризация()
{
List<ОбитательЗоопарка> обитатели = // ...
foreach (var обитатель in обитатели)
if (обитатель is IЖивотное) // отделяем животных от неживотных
ДобавитьЖивотное((IЖивотное)обитатель);
}
void ДобавитьЖивотное(IЖивотное животное) // сюда сможет попасть только животное
{
...
Мне кажется достаточно любопытным, что данный вопрос помечен тегом «ооп», но при этом во многих ответах явно просачиваются специфические аспекты конкретных языков программирования. Я же постараюсь дать ответ исходя из понятий ООП и лишь потом показать, почему вообще это различие появилось в некоторых языках программирования.
Абстрактные классы и интерфейсы имеют определенное отношение к наследованию, точнее к моделированию мира. С их помощью мы хотим выразить, что у определенной группы вещей в нашей системе есть что-то общее: некоторое общее поведение, которое отличает эту группу штуковин от всех остальных.
Допустим, для примера, мы хотим смоделировать кнопки в интерфейсе пользователя. Поскольку мы говорим об ООП, то мы выделим некоторый тип Кнопки с некоторым набором операций (которые определяют поведение) и скрытого состояния, на которое опирается поведение (да, скрытого состояния может и не быть). При этом мы можем выделить три вида операции:
Конкретная фиксированная операция, которая должна быть абсолютно стабильно для всех типов кнопок.
Конкретная операция с поведением по умолчанию (т.е. операция, чье поведение подходит для многих типов кнопок, но могут быть кнопки с другим поведением).
Декларация операции без конкретной реализации (т.е. операция, чье поведение определить невозможно, поскольку на этом этапе не известно разумное поведение по умолчанию или же операции могут слишком сильно различаться у разных кнопок).
Другими словами, тип Кнопки может содержать невиртуальные методы (non-virtual methods), виртуальные методы (virtual methods) и абстрактные методы (abstract methods).
Наличие разных типов методов является очень важным инструментом моделирования и позволяет весьма точно выражать намерения проектировщика. Например, мы можем добавить невиртуальную операцию «Нажатия на кнопку», которая будет делегировать часть своей работы виртуальному (или абстрактному методу) «Обработать нажатие», но при этом всегда выполнять определенную часть работы (прозорливый читатель увидит в этом описании паттерн «Шаблонный метод»).
После того, как мы определили базовый тип, пришло время определить произвольные типы. И тут начинаются вопросы. Точнее, вопросов никаких не возникает, когда у типа есть лишь один непосредственный базовый тип или все базовые типы содержат лишь декларации операций. Не проблема, унаследовать «Кнопку меню» от «Кнопки» и переопределить метод «Нажать на кнопку». Но что, если наш тип «Кнопка меню» будет отнаследован от двух типов с одной и той же виртуальной операцией? Как переопределить лишь одну, а оставить другую? А как быть клиенту нового типа и различить, какую операцию вызвать? А что если у двух базовых типов есть поле с одним именем? А что если у одного базового типа метод «Нажать кнопку» реализован, а у другого – лишь описан в виде декларации?
Нет, все эти проблемы решаемы, и в С++, и Eiffel, и других языках программирования вы можете довольно гибко контролировать, что и как переопределять, что прятать, что выставлять наружу и как вызвать метод определенного базового типа. Но для авторов некоторых языков программирования подобная сложность показалась излишней, и они пошли на хитрость и отделили типы, которые содержат лишь декларации методов в отдельную категорию, и так появились интерфейсы.
Теперь будет легко провести разницу между тремя понятиями – интферфейса, абстрактного базового класса и конкретного базового класса.
Интерфейс – описывает некоторое семейство типов и содержит лишь декларации операций (да, я осознанно пишу слово «декларация», а не использую слово «контракт», которое в ООП имеет вполне определенное значение).
Абстрактный базовый класс описывает некоторое семейство типов, но помимо декларации операций может содержать реализации по умолчанию (виртуальные методы) и фиксированные операции (невиртуальные методы).
Конкретный класс описывает некоторое семейство типов, которое готово для использования клиентами. Такой класс не может содержать декларации операций и все его операции должны быть либо фиксированными (невиртуальные методы) или содержать реализацию по умолчанию (виртуальные методы). Есть еще один подвид конкретных классов – запечатанный (sealed) класс – это разновидность конкретного класса отнаследоваться от которого невозможно, а значит он может содержать лишь конкретные операции.
Выделение интерфейсов в отдельную категорию полезно не только с точки зрения упрощения реализации языков программирования, но и для выделения разных подходов к моделированию. Так, например, наследование классов моделирует отношение «Является» («Кнопка меню» ЯВЛЯЕТСЯ «Кнопкой»), а базовые классы обычно содержат определенный функционал, тесно связанный с функционалом производного класса. Базовые классы не просто моделируют группу типов, но и позволяют использовать повторно существующий функционал.
Интерфейсы же, по своей природе обладают меньшей связностью (low coupling), поскольку не обладают конкретным поведением, которое может осложнить жизнь класса-наследника. Интерфейсы также могут моделировать отношение «Является» («Кнопка меню» ЯВЛЯЕТСЯ «IКнопкой»), но могут определять и менее жесткое отношение «Может выполнять роль» (CAN DO). Например, интерфейс IEquatable из BCL определяет «дополнительное» поведение, которое говорит о возможности типов сравнивать значения объектов.
Давайте по порядку. Интерфейс - это договор/объязательства, которые берёт на себя класс и обязан их выполнить. Допустим: есть массив классов, которые умеют здороваться
std::vector <IHellower*> mArr;
class IHellower{
virtual void Hello() = 0;
}
Теперь любой класс вне зависимости от его сложности иерархии и объема может унаследовать (взять на себя объязательство/подписать контракт на выполнения) данного метода и тем самым "станет своим через чужих". Самое приятное тут то, что каким бы обширным бы ни был интерфейс (в данном контексте имеются публичные методы класса) у класса выполняющего данный контракт - из этого массива будет видет только один метод. Пример: есть много графических классов и некоторые из них вы научили реагировать на Blur - запихните их в 1 массив интерфейсов и при надобности вызывайте метод Blur(int value); При использовании интерфейса, мы абстрагируемся от того, что за класс: машина, растение, воздух ... Если он взял на себя обязательства иметь реализацию "Поздороваться" у нас есть гарантия, что данный метод будет нам доступен через объект (указатель данного класса) (напоминаю, интерфейс чистовиртуальные методы, в паблик секции, без переменных).
А теперь другая стороная, С++ при всём своем могуществе не имеет понятия интерфейс на уровне языка. Но программисты привыкли к интерфейсу, это удобно, это безопасно (почитайте про Insersion of control) и правильно (с точки зрения архитектуры). Приходится реализовывать "интерфейс" теми инструментами, что предлагает язык С++ - это абстракный класс. Как вы заметили, туда можно запихнуть разные методы (public/protected/private), класс-мемберов(в других языках за переменные в интерфейсе уже проблемы). Было бы ключевое слово interface в С++ и наследовалось бы внятно как в C# - такой бы путаницы небыло. Абстрактные классы, как раз, и применяются в случаях выделения общей части классов, но самого такого класса как бы не должно быть (это бессмыслено иметь экземпляр такого класа).
class SomeThingLikeABall
{
private:
int radius;
int weight;
public:
bool canPlayFootballWithIt() = 0;
void draw() = 0;
}
Как пример, описывали классы мяч и арбуз, нашли общие черты, увидели, что еще будут описывать круглые предметы. Выделили всё это в общий класс и сделали его абстрактным, потому что зачем в системе иметь возможность иметь класс "Нечто кругло, с весом и можно кинуть".
Отличия абстрактного класса от интерфейса в Java
Можно еще добавить, что часто класс описывает некоторую модель, а интерфейс - поведение или роль. Класс называют существительным именем (Dog, Car, House), а к интерфейсу добавляют окончание -able, чтобы описывало некоторое действие или свойство (Moveable, Eatable, Buildable). Например, мы создаем игру в жанре стратегия. У нас есть разные фракции, в каждой из них есть разные юниты (воины, строители) и мы для каждого юнита создаем отдельный класс. Но в поведении между всеми этими юнитами есть много общего - у любого воина независимо от фракции есть способность драться (Attackable), у любого строителя независимо от фракции есть способность строить (Buildable), а у абсолютно всех юнитов есть способность двигаться (Moveable). Таким образом можно описать взаимодействие всех ролей с помощью множественного наследования интерфейсов.
Можно я влезу )
класс с виртуальной функцией/функциями называется - абстрактным
абстрактный класс может иметь одну или несколько чисто виртуальных функций
если абстрактный класс имеет хотя бы одну чисто виртуальную функцию, объект такого класса нельзя создавать, а только наследовать причем чисто виртуальные функции должны быть переопределены в будущем.
интерфейс класса - это указание/договор с программистом о том, как программист может использовать этот класс. т.е. получается интерфейс - это все открытые (public) данные к которым может обратится программист.
по сути написал что и @VladD только простым языком
ps - модераторам почему я комментировать не могу?
Абстрактный класс может иметь переменные, конструкторы, свойства и реализации методов. Интерфейс - только сигнатуры методов.
Interface - это класс, который по умолчанию является абстрактным. По сути интерфейс нужен для того, чтобы от него наследоваться.
Абстрактный класс - это класс, который объявляется абстрактным - может включать или не включать абстрактные методы. Абстрактные классы не могут быть созданы, но они могут быть подклассами. Когда абстрактный класс является подклассом, подкласс обычно предоставляет реализации для всех абстрактных методов в его родительском классе.
Я немного расшифрую понятие абстрактного класса. В связи с вопросом почему я использую абстрактный класс. Иногда я просто добавляю фразу abstract к классу, по причине того что некоторые методы должны имплементироваться пользоватем этого класса и указываю какие иммено методы должны быть абстрактными. Для чего это нужно, впрочем если пользователь будет пользоваться абстрактными классом, то часть его методов уже имеет дефолтную имплементацию, которую можно перекрыть, а абстрактные методы также перекрываются, только синтаксис предполагает наличие модификатора abstract и отсутствие тела метода, определяя тем самым только сигнатуру. Это свойство языка и отличие от других объектно-ориентированных языков. Фраза abstract не обязательно предполагает абстрактность или такое толкование, а лишь возможность указания методов, которые необходимо перекрывать в классах наследниках.
В объектно-ориентированном программировании абстракция является одним из трех основных принципов (наряду с инкапсулированием и наследованием). Благодаря процессу абстракции программист скрывает все, кроме соответствующих данных об объекте, чтобы уменьшить сложность и повысить эффективность.
Может ли абстрактный класс не иметь абстрактных методов?Да, мы можем иметь абстрактный класс без абстрактных методов, поскольку оба являются независимыми понятиями. Объявление абстрактного класса означает, что он не может быть создан сам по себе и может быть только подклассифицирован. Объявление абстрактного метода означает, что метод будет определен в подклассе.
Можете ли вы создать объект абстрактного класса в Java?Если мы создадим объект абстрактного класса и назовем метод без тела (поскольку метод является чисто виртуальным), он даст ошибку. Вот почему мы не можем создать объект абстрактного класса. Короче говоря, законно иметь публичный конструктор абстрактного класса. Абстрактный класс нельзя создать, но можно пользоваться если есть соответствующая имплементация этого класса. Сама абстракция в отличие от наследования предполагает отсутствие кода имплементации. Если вы используете абстрактный класс то вы можете ограничиться декларированием методов. Интерфейсы это такие абстрактные классы у которых все методы абстрактные.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости