OOP Course
Сьогодні

Підрозділ 7.4

Успадкування інтерфейсів

Пояснює успадкування інтерфейсів, приховування методів через new і правила доступності похідних інтерфейсів.

7.4. Успадкування інтерфейсів

Інтерфейси не лише описують контракти — вони можуть розширювати один одного. Механізм успадкування інтерфейсів (interface inheritance) дозволяє будувати ієрархії контрактів: від загального до конкретного. Клас, який реалізує похідний інтерфейс, зобов'язаний виконати контракти всіх інтерфейсів у ланцюжку.

Це дає змогу проектувати системи поступово: спочатку визначати базові здібності об'єкта, потім нашаровувати на них більш спеціалізовані вимоги — не змінюючи вже написаний код.

Базовий синтаксис

Синтаксис успадкування інтерфейсів ідентичний синтаксису успадкування класів — через двокрапку:

interface IExaminable
{
    void Examine();       // провести огляд пацієнта
}

interface IDiagnosable : IExaminable
{
    void Diagnose();      // поставити діагноз
}

Тут IDiagnosable розширює IExaminable. Це означає, що IDiagnosable включає в себе всі члени IExaminable плюс власні.

Будь-який клас чи структура, що реалізує IDiagnosable, зобов'язана реалізувати обидва методи — і Examine(), і Diagnose():

Зверніть увагу: змінна типу IExaminable дає доступ лише до Examine(), хоч і містить той самий об'єкт. Тип змінної визначає, який «зріз» контракту видно у конкретному місці коду.

Ланцюжок успадкування

Інтерфейси можуть утворювати ланцюжки довільної довжини. Кожен новий інтерфейс у ланцюжку додає нові вимоги до контракту:

Ланцюжок успадкування інтерфейсів

Клас Doctor, що реалізує ITreatable, зобов'язаний надати реалізацію всіх трьох методів — Examine(), Diagnose() і Treat(). Компілятор перевіряє це під час збірки: якщо хоча б один метод не реалізовано — отримаємо помилку компіляції.

Множинне успадкування інтерфейсів

На відміну від класів, інтерфейс може успадковувати відразу кілька батьківських інтерфейсів. Це одна з ключових відмінностей інтерфейсів від класів у C# — множинне успадкування реалізації заборонене для класів, але множинне успадкування контракту — цілком допустиме для інтерфейсів.

Синтаксис: після двокрапки перераховуємо всі базові інтерфейси через кому:

interface IClinicAppointment : ISchedulable, IPayable
{
    // ...
}

Множинне успадкування інтерфейсів

Клас Appointment, що реалізує IClinicAppointment, зобов'язаний виконати контракти обох батьківських інтерфейсів — ISchedulable і IPayable — а також власні члени IClinicAppointment:

Ключове слово new — приховування членів базового інтерфейсу

Коли базовий інтерфейс має метод із реалізацією за замовчуванням (default interface implementation), похідний інтерфейс може її перевизначити за допомогою ключового слова new. Це приховування (hiding), а не перевизначення у звичному розумінні.

Поведінка залежить від типу змінної, а не від фактичного об'єкта. Якщо змінна оголошена як IDiagnosable — буде викликана його версія Examine(). Якщо IExaminable — оригінальна. Це принципово відрізняється від поліморфізму через virtual/override, де завжди викликається версія фактичного об'єкта.

Клас може повністю взяти контроль і реалізувати Examine() самостійно — тоді обидва типи повертатимуть ту саму реалізацію:

Модифікатори sealed та abstract для інтерфейсів

Інтерфейси мають власні правила щодо модифікаторів доступу та структури:

sealed — заборонений для інтерфейсів. Модифікатор sealed у класах забороняє успадкування. Для інтерфейсів такого механізму не існує — будь-який інтерфейс можна успадкувати. Це відповідає природі інтерфейсу як відкритого контракту.

abstract — надлишковий для інтерфейсів. Інтерфейс вже за визначенням є абстрактним: він описує контракт без (обов'язкової) реалізації. Тому ключове слово abstract на рівні інтерфейсу не має сенсу і не допускається компілятором.

Правила рівня доступу

При успадкуванні інтерфейсів діє таке саме правило, що й при успадкуванні класів: похідний інтерфейс не може бути менш обмеженим, ніж базовий.

Припустимо, що базовий інтерфейс є public — тоді похідний може бути або public, або internal:

public interface IExaminable
{
    void Examine();
}

// Коректно: internal є більш суворим, ніж public
internal interface IDiagnosable : IExaminable
{
    void Diagnose();
}

Але не навпаки. Якщо базовий є internal, то похідний не може бути public — це порушує принцип інкапсуляції типів:

internal interface IExaminable
{
    void Examine();
}

// ПОМИЛКА: IRunAction може бути лише internal,
// бо базовий IExaminable є internal
public interface IDiagnosable : IExaminable
{
    void Diagnose();
}

Компілятор поверне помилку: неможливо оголосити public тип, що успадковує internal тип, оскільки internal тип недоступний за межами збірки.

Підсумок

Успадкування інтерфейсів — потужний інструмент для побудови гнучких та розширюваних систем. Основні правила:

  • Інтерфейс успадковує члени всіх своїх базових інтерфейсів.
  • Клас, що реалізує похідний інтерфейс, зобов'язаний реалізувати весь ланцюжок.
  • Інтерфейс може успадковувати кілька базових інтерфейсів одночасно.
  • sealed і abstract на рівні інтерфейсу не допускаються.
  • new у похідному інтерфейсі приховує (а не перевизначає) метод базового.
  • Доступ похідного інтерфейсу не може бути менш суворим, ніж базового.
Розроблено Tomka Yurii · © 2026 ·