Интерфе́йс (англ. interface) — структура программы/синтаксиса, определяющая отношение с объектами, объединенными только некоторым поведением. При проектировании классов, разработка интерфейса тождественна разработке спецификации (множества методов, которые каждый класс, использующий интерфейс, должен реализовывать).
 Интерфейсы, наряду с абстрактными классами и протоколами, устанавливают взаимные обязательства между элементами программной системы, что является фундаментом концепции программирования по контракту (англ. design by contract, DbC). Интерфейс определяет границу взаимодействия между классами или компонентами, специфицируя определённую абстракцию, которую осуществляет реализующая сторона.
 Интерфейс в ООП является строго формализованным элементом объектно-ориентированного языка и широко используется в исходном коде программ.
 Интерфейсы позволяют наладить множественное наследование объектов и в то же время решить проблему ромбовидного наследования. В языке C++ она решается через наследование классов с использованием ключевого слова virtual.
 Описание ООП-интерфейса, если отвлечься от деталей синтаксиса конкретных языков, состоит из двух частей: имени и методов интерфейса.
 Использование интерфейсов возможно двумя способами:
 Как правило, в объектно-ориентированных языках программирования интерфейсы, как и классы, могут наследоваться друг от друга. В этом случае интерфейс-потомок включает все методы интерфейса-предка и, возможно, добавляет к ним свои собственные.
 Таким образом, с одной стороны, интерфейс — это «договор», который обязуется выполнить класс, реализующий его, с другой стороны, интерфейс — это тип данных, потому что его описание достаточно чётко определяет свойства объектов, чтобы наравне с классом типизировать переменные. Следует, однако, подчеркнуть, что интерфейс не является полноценным типом данных, так как он задаёт только внешнее поведение объектов. Внутреннюю структуру и реализацию заданного интерфейсом поведения обеспечивает класс, реализующий интерфейс; именно поэтому «экземпляров интерфейса» в чистом виде не бывает, и любая переменная типа «интерфейс» содержит экземпляры конкретных классов.
 Использование интерфейсов — один из вариантов обеспечения полиморфизма в объектных языках и средах. Все классы, реализующие один и тот же интерфейс, с точки зрения определяемого ими поведения, ведут себя внешне одинаково. Это позволяет писать обобщённые алгоритмы обработки данных, использующие в качестве типов параметры интерфейсов, и применять их к объектам различных типов, всякий раз получая требуемый результат.
 Например, интерфейс «Cloneable» может описать абстракцию клонирования (создания точных копий) объектов, специфицировав метод «Clone», который должен выполнять копирование содержимого объекта в другой объект того же типа. Тогда любой класс, объекты которого может понадобиться копировать, должен реализовать интерфейс Cloneable и предоставить метод Clone, а в любом месте программы, где требуется клонирование объектов, для этой цели у объекта вызывается метод Clone. Причём, использующему этот метод коду достаточно иметь только описание интерфейса, он может ничего не знать о фактическом классе, объекты которого копируются. Таким образом, интерфейсы позволяют разбить программную систему на модули без взаимной зависимости кода.
 «Чистый» интерфейс содержит только названия функций, которые будут определены где-то в классе-потомке, без тел. Однако для удобства программиста языки программирования и требования к коду могут допускать в интерфейсах определённые виды данных и функций.
 В любом случае интерфейс не может содержать:
 Можно заметить, что интерфейс, с формальной точки зрения, — это просто чистый абстрактный класс, то есть класс, в котором не определено ничего, кроме абстрактных методов. Если язык программирования поддерживает множественное наследование и абстрактные методы (как, например, C++), то необходимости во введении в синтаксис языка отдельного понятия «интерфейс» не возникает. Данные сущности описываются с помощью абстрактных классов и наследуются классами для реализации абстрактных методов.
 Однако поддержка множественного наследования в полном объёме достаточно сложна и вызывает множество проблем, как на уровне реализации языка, так и на уровне архитектуры приложений. Введение понятия интерфейсов является компромиссом, позволяющим получить многие преимущества множественного наследования (в частности, возможность удобно определять логически связанные наборы методов в виде сущностей, подобных классам и допускающих наследование и реализацию), не реализуя его в полном объёме и не сталкиваясь, таким образом, с большинством связанных с ним трудностей.
 На уровне исполнения классическая схема множественного наследования вызывает дополнительный ряд неудобств:
 Использование схемы с интерфейсами (вместо множественного наследования) позволяет отбросить эти проблемы, если не считать вопроса о вызове интерфейсных методов (то есть виртуальных вызовов методов при множественном наследовании, см. выше). Классическое решение состоит в том (например, в JVM для Java или CLR для C#), что интерфейсные методы вызываются менее эффективным способом, без помощи виртуальной таблицы: при каждом вызове сначала определяется конкретный класс объекта, а затем в нём ищется нужный метод (разумеется, с многочисленными оптимизациями).
 Как правило, языки программирования разрешают наследовать интерфейс от нескольких интерфейсов-предков. Все методы, объявленные в интерфейсах-предках, становятся частью объявления интерфейса-потомка. В отличие от наследования классов, множественное наследование интерфейсов гораздо проще реализуется и не вызывает существенных затруднений.
 Тем не менее, одна коллизия при множественном наследовании интерфейсов и при реализации нескольких интерфейсов одним классом всё-таки возможна. Она возникает, когда в двух или более интерфейсах, наследуемых новым интерфейсом или реализуемых классом, имеются методы с одинаковыми сигнатурами. Разработчики языков программирования вынуждены выбирать для таких случаев те или иные способы разрешения противоречий. Вариантов здесь несколько: запрет на реализацию, явное указание конкретного и реализация базового интерфейса или класса.
 Реализация интерфейсов во многом определяется исходными возможностями языка и целью, с которой интерфейсы введены в него. Очень показательны особенности использования интерфейсов в языках Java, Object Pascal системы Delphi и C++, поскольку они демонстрируют три принципиально разные ситуации: изначальная ориентация языка на использование концепции интерфейсов, их применение для совместимости и их эмуляция классами.
 В Delphi интерфейсы были введены для поддержки технологии COM фирмы Microsoft. Однако при выпуске Kylix интерфейсы как элемент языка были «отвязаны» от технологии COM. Все интерфейсы наследуются от интерфейса IInterface , который на платформе win32 совпадает с IUnknown, стандартным одноимённым COM-интерфейсом, подобно тому, как все классы в нём являются наследниками класса TObject. Явное использование в качестве предка IUnknown оставлено для кода, использующего технологию COM.
 Пример объявления интерфейса:
 Для того, чтобы объявить о реализации интерфейсов, в описании класса необходимо указать их имена в скобках после ключевого слова class, после имени класса-предка. Так как «интерфейс — это договор, который нужно выполнить», программа не компилируется, пока в реализующем классе не будет реализована procedure DoSomething;
 Вышеупомянутая ориентированность интерфейсов Delphi на технологию COM привела к некоторым неудобствам. Дело в том, что интерфейс IInterface (от которого наследуются все остальные интерфейсы) уже содержит три обязательных для COM-интерфейсов метода: QueryInterface, _AddRef, _Release. Следовательно, любой класс, реализующий любой интерфейс, обязан реализовать эти методы, даже если по логике программы интерфейс и класс не имеют никакого отношения к COM. Необходимо заметить, что данные три метода также используются для контроля времени жизни объекта и реализации механизма запроса интерфейса через оператор «as».
 Пример класса, реализующего интерфейс:
 Программист должен правильно реализовать методы QueryInterface, _AddRef, _Release. Чтобы избавиться от необходимости писать стандартные методы, предусмотрен библиотечный класс TInterfacedObject — он реализует три вышеупомянутых метода, и любой класс, наследуемый от него и его потомков, получает эту реализацию. Реализация этих методов в TInterfacedObject предполагает автоматический контроль за временем жизни объекта путём подсчета ссылок через методы _AddRef и _Release, которые вызываются автоматически при входе в область видимости и выходе из неё.
 Пример класса — наследника TInterfacedObject:
 При наследовании класса, реализующего интерфейс, от класса без интерфейсов, программист должен реализовать упомянутые методы вручную, определив наличие либо отсутствие контроля по подсчету ссылок, а также получение интерфейса в QueryInterface.
 Пример произвольного класса без подсчета ссылок:
 C++ поддерживает множественное наследование и абстрактные классы, поэтому, как уже упоминалось выше, отдельная синтаксическая конструкция для интерфейсов в этом языке не нужна. Интерфейсы определяются при помощи абстрактных классов, а реализация интерфейса производится путём наследования этих классов.
 Пример определения интерфейса:
 Интерфейс реализуется через наследование (благодаря наличию множественного наследования возможно реализовать в одном классе несколько интерфейсов, если в этом есть необходимость; в примере ниже наследование не множественное):
 Тестируем всё вместе:
 В отличие от C++, Java не позволяет наследовать больше одного класса. В качестве альтернативы множественному наследованию существуют интерфейсы. Каждый класс в Java может реализовать любой набор интерфейсов. Порождать объекты от интерфейсов в Java нельзя.
 Объявление интерфейсов очень похоже на упрощённое объявление классов.
 Оно начинается с заголовка. Сначала указываются модификаторы. Интерфейс может быть объявлен как public, и тогда он будет доступен для общего использования, либо модификатор доступа может не указываться, в этом случае интерфейс доступен только для типов своего пакета. Модификатор abstract для интерфейса не требуется, поскольку все интерфейсы являются абстрактными классами. Его можно указать, но делать этого не рекомендуется, чтобы не загромождать код.
 Далее записывается ключевое слово interface и имя интерфейса.
 После этого может следовать ключевое слово extends и список интерфейсов, от которых будет наследоваться объявляемый интерфейс. Родительских типов (классов и/или интерфейсов) может быть много — главное, чтобы не было повторений, и чтобы отношение наследования не образовывало циклической зависимости.
 Наследование интерфейсов действительно очень гибкое. Так, если есть два интерфейса, A и B, причем B наследуется от A, то новый интерфейс C может наследоваться от них обоих. Впрочем, понятно, что при наследовании от B, указание наследования от A является избыточным, так как все элементы этого интерфейса и так будут получены по наследству через интерфейс B.
 Затем в фигурных скобках записывается тело интерфейса.
 Пример объявления интерфейса (Ошибка если Colorable и Resizable классы: The type Colorable and Resizable cannot be a superinterface of Drawable; a superinterface must be an interface):
 Тело интерфейса состоит из объявления элементов, то есть полей-констант и абстрактных методов. Все поля интерфейса автоматически являются public final static, так что эти модификаторы указывать необязательно и даже нежелательно, чтобы не загромождать код. Поскольку поля являются финальными, необходимо их сразу инициализировать.
 Все методы интерфейса являются public abstract, и эти модификаторы также необязательны.
 Как видно, описание интерфейса гораздо проще, чем объявление класса.
 Для реализации интерфейса он должен быть указан при декларации класса с помощью ключевого слова implements. Пример:
 Каждый класс может реализовать любые доступные интерфейсы. При этом в классе должны быть реализованы все абстрактные методы, появившиеся при наследовании от интерфейсов или родительского класса, чтобы новый класс мог быть объявлен неабстрактным.
 Если из разных источников наследуются методы с одинаковой сигнатурой, то достаточно один раз описать реализацию, и она будет применяться для всех этих методов. Однако, если у них различное возвращаемое значение, то возникает конфликт. Пример:
 В C# интерфейсы могут наследовать один или несколько других интерфейсов. Членами интерфейсов могут быть методы, свойства, события и индексаторы:
 При реализации интерфейса класс должен реализовать как методы самого интерфейса, так и его базовых интерфейсов:
 Интерфейсы в UML используются для визуализации, специфицирования, конструирования и документирования стыковочных UML-узлов между составными частями системы. Типы и UML-роли обеспечивают механизм моделирования статического и динамического соответствия абстракции интерфейсу в конкретном контексте.
 В UML интерфейсы изображаются как классы со стереотипом «interface», либо в виде кружочков (в этом случае содержащиеся в интерфейсе UML-операции не отображаются).