Интерфейс против абстрактного класса (общий ОО) - PullRequest
1329 голосов
/ 17 апреля 2009

Недавно у меня было два телефонных интервью, где меня спрашивали о различиях между интерфейсом и абстрактным классом. Я объяснил каждый их аспект, который мог придумать, но, похоже, они ждут, чтобы я упомянул что-то конкретное, и я не знаю, что это такое.

Исходя из своего опыта, я думаю, что верно следующее. Если я не понял, пожалуйста, дайте мне знать.

Интерфейс:

Каждый метод, объявленный в Интерфейсе, должен быть реализован в подклассе. В интерфейсе могут существовать только события, делегаты, свойства (C #) и методы. Класс может реализовывать несколько интерфейсов.

Абстрактный класс:

Только абстрактные методы должны быть реализованы подклассом. Класс Abstract может иметь нормальные методы с реализациями. Абстрактный класс также может иметь переменные класса, кроме событий, делегатов, свойств и методов. Класс может реализовать только один абстрактный класс только из-за отсутствия Multi-наследования в C #.

  1. После всего этого интервьюер задал вопрос: «Что если бы у вас был класс Abstract только с абстрактными методами? Как это отличалось бы от интерфейса?» Я не знал ответа, но я думаю, что это наследство, как упомянуто выше, верно?

  2. Другой интервьюер спросил меня, что если бы у вас была переменная Public внутри интерфейса, как бы она отличалась от абстрактного класса? Я настаивал на том, чтобы внутри интерфейса не было публичной переменной. Я не знал, что он хотел услышать, но он тоже не был удовлетворен.

См. Также :

Ответы [ 34 ]

820 голосов
/ 30 августа 2010

Как насчет аналогии: когда я был в ВВС, я прошел подготовку пилотов и стал пилотом ВВС США (ВВС США). В тот момент я не был квалифицирован, чтобы летать на чем-либо, и должен был посещать обучение по типу самолета. После того, как я получил квалификацию, я был пилотом (абстрактный класс) и пилотом C-141 (конкретный класс). На одном из моих назначений мне дали дополнительную обязанность: офицер безопасности. Теперь я все еще был пилотом и пилотом C-141, но я также выполнял обязанности сотрудника по безопасности (я, так сказать, реализовал ISafetyOfficer). Пилот не обязан был быть офицером безопасности, другие могли бы это сделать.

Все пилоты ВВС США должны следовать определенным правилам военно-воздушных сил, а все пилоты C-141 (или F-16, или T-38) являются «пилотами ВВС США». Любой может быть офицером безопасности. Итак, подведем итог:

  • Пилот: абстрактный класс
  • C-141 Pilot: класс бетона
  • Сотрудник по безопасности: интерфейс

добавлено примечание: это должна была быть аналогия, помогающая объяснить концепцию, а не рекомендация по кодированию. Смотрите различные комментарии ниже, обсуждение интересно.

698 голосов
/ 17 апреля 2009

Хотя ваш вопрос указывает на то, что он относится к «общепринятым нормам», похоже, он действительно фокусируется на использовании этих терминов в .NET.

В .NET (аналогично для Java):

  • интерфейсы не могут иметь состояния или реализации
  • класс, который реализует интерфейс, должен обеспечивать реализацию всех методов этого интерфейса
  • абстрактные классы могут содержать состояние (члены данных) и / или реализацию (методы)
  • абстрактные классы могут наследоваться без реализации абстрактных методов (хотя такой производный класс сам является абстрактным)
  • интерфейсы могут быть множественным наследованием, абстрактные классы могут не существовать (это, вероятно, основная конкретная причина существования интерфейсов отдельно от абстрактных классов - они допускают реализацию множественного наследования, которая устраняет многие проблемы общего MI).

Как общие термины ОО, различия не обязательно четко определены. Например, есть программисты на C ++, которые могут иметь похожие жесткие определения (интерфейсы являются строгим подмножеством абстрактных классов, которые не могут содержать реализацию), в то время как некоторые могут сказать, что абстрактный класс с некоторыми реализациями по умолчанию все еще является интерфейсом или что неабстрактный класс все еще может определять интерфейс.

Действительно, существует идиома C ++, называемая не-виртуальным интерфейсом (NVI), где открытые методы - это не виртуальные методы, которые «одобряются» для частных виртуальных методов:

210 голосов
/ 31 августа 2011

Я думаю, что ответ, который они ищут, - это принципиальное различие или философское различие OPPS.

Наследование абстрактного класса используется, когда производный класс разделяет основные свойства и поведение абстрактного класса. Тип поведения, который на самом деле определяет класс.

С другой стороны, наследование интерфейса используется, когда классы имеют периферийное поведение, которое не обязательно определяет производный класс.

Например, Автомобиль и Грузовик имеют много основных свойств и поведения абстрактного класса Automobile, но они также имеют некоторое периферийное поведение, такое как Генерация выхлопа, которое разделяют даже не автомобильные классы, такие как Drillers или PowerGenerators, и не обязательно определяют Автомобиль или Грузовик. Таким образом, Car, Truck, Driller и PowerGenerator могут использовать один и тот же интерфейс IExhaust.

185 голосов
/ 05 апреля 2012

Short: абстрактные классы используются для моделирования иерархии классов похожих классов (например, Animal может быть абстрактным классом, а Human, Lion, Tiger могут быть конкретными производными классами)

И

Интерфейс используется для Связи между 2 подобными / не похожими классами, который не заботится о типе класса, реализующего Интерфейс (например, Высота может быть свойством интерфейса, и это может быть реализовано Human, Building, Tree Не имеет значения, можете ли вы есть, можете ли вы плавать, можете ли умереть или что-то еще ... это имеет значение только для того, что вам нужно для высоты (реализация в вашем классе)).

77 голосов
/ 17 апреля 2009

Есть несколько других отличий -

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

В одном классе может быть реализовано более одного интерфейса. Класс может быть производным только от одного абстрактного базового класса. Это допускает полиморфную иерархию с использованием интерфейсов, но не абстрактных базовых классов. Это также учитывает псевдо-мульти-наследование с использованием интерфейсов.

Абстрактные базовые классы могут быть изменены в v2 + без нарушения API. Изменения в интерфейсах являются критическими изменениями.

[C # /. NET Specific] Интерфейсы, в отличие от абстрактных базовых классов, могут применяться к типам значений (структурам). Структуры не могут наследоваться от абстрактных базовых классов. Это позволяет применять поведенческие контракты / рекомендации по использованию к типам значений.

66 голосов
/ 14 июня 2013

Наследование
Рассмотрим машину и автобус. Это два разных автомобиля. Но, тем не менее, они имеют некоторые общие свойства, такие как рулевое управление, тормоза, шестерни, двигатель и т. Д.
Таким образом, с концепцией наследования это можно представить следующим образом ...

public class Vehicle {
    private Driver driver;
    private Seat[] seatArray; //In java and most of the Object Oriented Programming(OOP) languages, square brackets are used to denote arrays(Collections).
    //You can define as many properties as you want here ...
}

Теперь велосипед ...

public class Bicycle extends Vehicle {
    //You define properties which are unique to bicycles here ...
    private Pedal pedal;
}

И Автомобиль ...

public class Car extends Vehicle {
    private Engine engine;
    private Door[] doors;
}

Это все о Наследование . Мы используем их для классификации объектов на более простые формы Base и их потомков, как мы видели выше.

Абстрактные классы

Абстрактные классы - это неполные объекты. Чтобы понять это далее, давайте еще раз рассмотрим аналогию с автомобилем.
Автомобиль можно водить. Правильно? Но разные транспортные средства управляются по-разному ... Например, вы не можете водить машину так же, как водите велосипед.
Итак, как представить функцию движения транспортного средства? Труднее проверить, что это за тип транспортного средства, и управлять им со своей функцией; вам придется менять класс водителя снова и снова при добавлении нового типа транспортного средства.
Здесь идет роль абстрактных классов и методов. Вы можете определить метод drive как абстрактный, чтобы сказать, что каждый наследующий потомок должен реализовать эту функцию.
Так что если вы измените класс автомобиля ...

//......Code of Vehicle Class
abstract public void drive();
//.....Code continues

Велосипед и автомобиль должны также указать, как им управлять. В противном случае код не скомпилируется, и выдается ошибка.
Вкратце ... абстрактный класс - это частично неполный класс с некоторыми неполными функциями, которые наследующие потомки должны указывать свои собственные.

Интерфейсы Интерфейсы полностью неполные. У них нет никаких свойств. Они просто указывают на то, что наследующие дети способны что-то делать ...
Предположим, у вас есть разные типы мобильных телефонов. У каждого из них есть разные способы выполнять разные функции; Пример: позвонить человеку. Производитель телефона указывает, как это сделать. Здесь мобильные телефоны могут набирать номер, то есть он может набирать номер. Давайте представим это как интерфейс.

public interface Dialable {
    public void dial(Number n);
}

Здесь создатель Dialable определяет, как набирать номер. Вам просто нужно дать ему номер для набора.

// Makers define how exactly dialable work inside.

Dialable PHONE1 = new Dialable() {
    public void dial(Number n) {
        //Do the phone1's own way to dial a number
    }
}

Dialable PHONE2 = new Dialable() {
    public void dial(Number n) {
        //Do the phone2's own way to dial a number
    }
}


//Suppose there is a function written by someone else, which expects a Dialable
......
public static void main(String[] args) {
    Dialable myDialable = SomeLibrary.PHONE1;
    SomeOtherLibrary.doSomethingUsingADialable(myDialable);
}
.....

Таким образом, используя интерфейсы вместо абстрактных классов, разработчику функции, которая использует Dialable, не нужно беспокоиться о его свойствах. Пример: есть ли у него сенсорный экран или клавиатура, стационарный телефон или мобильный телефон. Вам просто нужно знать, можно ли набрать номер; наследует ли (или реализует) интерфейс Dialable.

И что еще более важно , если когда-нибудь вы переключите Dialable на другой

......
public static void main(String[] args) {
    Dialable myDialable = SomeLibrary.PHONE2; // <-- changed from PHONE1 to PHONE2
    SomeOtherLibrary.doSomethingUsingADialable(myDialable);
}
.....

Вы можете быть уверены, что код по-прежнему работает отлично, потому что функция, которая использует набор номера, не зависит (и не может) зависеть от деталей, отличных от указанных в интерфейсе набора номера. Они оба реализуют интерфейс Dialable, и это единственное, о чем заботится функция.

Интерфейсы обычно используются разработчиками для обеспечения функциональной совместимости (взаимозаменяемости) между объектами, поскольку они имеют общую функцию (точно так же, как вы можете перейти на стационарный или мобильный телефон, если вам просто нужно набрать номер). ). Короче говоря, интерфейсы - это гораздо более простая версия абстрактных классов без каких-либо свойств.
Также обратите внимание, что вы можете реализовать (наследовать) столько интерфейсов, сколько захотите, но вы можете расширять (наследовать) только один родительский класс.

Подробнее Абстрактные классы и интерфейсы

37 голосов
/ 27 ноября 2015

Если вы считаете java языком ООП для ответа на этот вопрос, выпуск Java 8 приводит к тому, что часть содержимого в приведенных выше ответах устареет. Теперь интерфейс Java может иметь методы по умолчанию с конкретной реализацией.

Сайт Oracle предоставляет ключевые различия между interface и abstract классом.

Рассмотрите возможность использования абстрактных классов , если:

  1. Вы хотите поделиться кодом между несколькими тесно связанными классами.
  2. Вы ожидаете, что классы, расширяющие ваш абстрактный класс, имеют много общих методов или полей или требуют модификаторов доступа, отличных от public (например, protected и private).
  3. Вы хотите объявить нестатические или не финальные поля.

Рассмотрите возможность использования интерфейсов , если:

  1. Вы ожидаете, что несвязанные классы будут реализовывать ваш интерфейс. Например, многие несвязанные объекты могут реализовать интерфейс Serializable.
  2. Вы хотите указать поведение определенного типа данных, но не беспокоитесь о том, кто реализует его поведение.
  3. Вы хотите воспользоваться множественным наследованием типа.

Проще говоря, я хотел бы использовать

интерфейс: Для реализации контракта несколькими несвязанными объектами

абстрактный класс: Для реализации одинакового или различного поведения среди нескольких связанных объектов

Посмотрите на пример кода, чтобы ясно понять вещи: Как мне объяснить разницу между интерфейсом и классом Abstract?

32 голосов
/ 17 апреля 2009

Интервьюеры лают странное дерево. Для языков, таких как C # и Java, есть разница, но в других языках, таких как C ++, нет. Теория ОО не различает их, а лишь синтаксис языка.

Абстрактный класс - это класс с реализацией и интерфейсом (чисто виртуальные методы), которые будут наследоваться. Интерфейсы обычно не имеют никакой реализации, а только чисто виртуальные функции.

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

31 голосов
/ 17 апреля 2009

Реализуя интерфейсы, вы получаете композицию (отношения «есть») вместо наследования (отношения «есть»). Это важный принцип, который нужно помнить, когда речь заходит о таких вещах, как шаблоны проектирования, когда вам нужно использовать интерфейсы для получения композиции поведения вместо наследования.

26 голосов
/ 31 мая 2014

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

  1. Когда мы должны использовать интерфейс?

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

  2. Когда нам следует использовать абстрактный класс?

    если вы знаете реализацию, но не полностью (частично реализацию), тогда мы переходим к классу Abstract.

    Интерфейс

    каждый метод по умолчанию public abstract означает, что интерфейс на 100% чистый аннотация.

    Аннотация

    может иметь метод Concrete и метод Abstract, то есть метод Concrete, который имеет реализацию в классе Abstract, Абстрактный класс - это класс, который объявлен как абстрактный - он может включать или не включать абстрактные методы.

    Интерфейс

    Мы не можем объявить интерфейс закрытым

    Q. Почему мы не объявляем интерфейс закрытым и защищенным?

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

    Метод интерфейса
    также мы не можем объявить интерфейс как закрытый, защищенный, окончательный, статический, синхронизированный, собственный ... ..... 1048 *

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

    Аннотация

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

    Интерфейс

    Переменные объявляются в Interface как открытая статическая финальная версия по умолчанию, поэтому мы также не объявляем переменную как private, protected.

    Модификатор Volatile также не применяется в интерфейсе, потому что переменная интерфейса по умолчанию является общедоступной статической конечной и конечной переменными. Вы не можете изменить значение, если оно присвоит значение переменной, а после объявления переменной в интерфейсе необходимо назначить переменную. 1064 *

    А переменная volatile - это изменения, поэтому она противоположна. в конечном итоге это причина того, что мы не используем переменную volatile в интерфейсе.

    Аннотация

    Абстрактная переменная нет необходимости объявлять открытую статическую финальную версию.

Надеюсь, эта статья будет полезна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...