Is-a

Is-a (англ. is-a, также «является частным случаем», «является видом», «является типом») — это термин, обозначающий отношение субсумпции в области онтологий, репрезентации знаний, объектно-ориентированного программирования и объектно-ориентированного анализа и проектирования. В данном контексте отношение «is-a» выражает вложенность абстракций (например, типов или классов), при которой один класс A является подклассом другого класса B (и, соответственно, B — надклассом класса A).

Другими словами, тип A является подтипом типа B, если спецификация A влечёт спецификацию B. То есть любой объект (или класс), удовлетворяющий спецификации A, автоматически удовлетворяет и спецификации B, поскольку последняя является более общей[1].

Например, кошка — это частный случай животного, но не наоборот. Все кошки — животные, однако далеко не все животные являются кошками. Поведение, общее для всех животных, определяется в классе животных, а поведение, характерное только для кошек — в классе кошек. Таким образом, при объявлении класса «Кошка» как специализации класса «Животное», все экземпляры класса «Кошка» наследуют поведение, определённое для животных, без необходимости повторного его описания.

Связанные понятия

Отношение «is-a» («является частным случаем»), в отличие от отношения «has-a» («имеет») между типами (классами), необходимо различать при проектировании моделей (например, компьютерной программы), чтобы не допустить ошибочного смешения иерархии наследования и иерархии композиции. Кроме того, отношение «is-a» отличается от отношения «является экземпляром» между объектами (экземплярами) и их типами или классами (см. различие типа и сущности). В современном объектно-ориентированном программировании предпочтение часто отдаётся композиции (отношение «has-a») перед наследованием («is-a»). Композиция обеспечивает слабую связанность, большую гибкость и значительно упрощает модульное тестирование, в частности, мокирование зависимостей[2].[3]

Краткое описание различных отношений:

  • гипероним-гипоним (надтип/надкласс — подтип/подкласс): отношения между типами (или классами), определяющие таксономическую иерархию. Для отношения субсумпции: гипоним (подтип, подкласс) находится в отношениях «is-a» со своим гиперонимом (надтипом, надклассом);
  • холонимия-меронимия («целое»/«контейнер» — «часть»/«элемент»): отношения между типами (или классами), определяющие иерархию владения и включения.

Для отношения агрегации (без владения): холоним (целое) находится в отношении «has-a» («имеет») со своим меронимом (частью). Для отношения композиции (с владением): мероним (конституент) находится в отношении «является частью» по отношению к своему холониму (целому). Для отношения containment (контейнирования): мероним (элемент) — в отношении «является членом» со своим холонимом (контейнером)[4].

  • концепт-объект (тип-экземпляр): отношение между типом (или классом) и его объектами (экземплярами), где экземпляр находится в отношении «является экземпляром» («instance of») по отношению к своему типу (классу).

В современных языках онтологий, таких как OWL 2, строгое различие между отношениями «is-a» (подкласс) и «instance-of» (экземпляр) может размываться благодаря механизму punning (каламбур). Этот механизм позволяет использовать один и тот же идентификатор одновременно в качестве класса и экземпляра для целей метамоделирования[5].

Примеры подтипизации

Подтипизация позволяет заменить один тип другим типом или абстракцией. Таким образом, подтипизация устанавливает отношение is-a между подтипом и определённой абстракцией — в зависимости от возможностей языка либо явно (эксплицитно), либо неявно. В языках программирования с поддержкой наследования как механизма подтипизации это отношение выражается непосредственно через наследование.

C++

Следующий пример на языке C++ демонстрирует явную связь наследования между классами A и B, где B — подкласс и одновременно подтип класса A. Класс B может быть использован везде, где ожидается класс A (через ссылку, указатель или сам объект):

class A
{ public:
   void DoSomethingALike() const {}
};

class B : public A
{ public:
   void DoSomethingBLike() const {}
};

void UseAnA(A const& some_A)
{
   some_A.DoSomethingALike();
}

void SomeFunc()
{
   B b;
   UseAnA(b); // b может быть подставлен вместо A
}

Современные стандарты C++ предоставляют новые механизмы для проверки отношений «is-a» на этапе компиляции. В стандарте C++20 был введён концепт std::derived_from, позволяющий строго валидировать публичное наследование[6]. В стандарте C++26 ожидается внедрение статической рефлексии, которая предоставит возможности для глубокого метапрограммного анализа иерархий классов[7].

Python

Следующий пример на языке Python показывает явное наследование между классами A и B. Класс B является подклассом и подтипом класса A, а потому может быть использован везде, где требуется объект типа A.

class A:
    def do_something_a_like(self):
        pass

class B(A):
    def do_something_b_like(self):
        pass

def use_an_a(some_a):
    some_a.do_something_a_like()

def some_func():
    b = B()
    use_an_a(b)  # b может быть подставлен вместо A

В следующем примере type(a) — «обычный» тип, а type(type(a)) — метатип. Хотя все встроенные типы Python имеют один и тот же метатип PyType_Type (который также является собственным метатипом), это не является обязательным: например, классические классы с типом types.ClassType можно рассматривать как самостоятельный метатип[8].

>>> a = 0
>>> type(a)
<type 'int'>
>>> type(type(a))
<type 'type'>
>>> type(type(type(a)))
<type 'type'>
>>> type(type(type(type(a))))
<type 'type'>

Начиная с версии Python 3.8 (PEP 544), отношение «is-a» может быть реализовано через структурную подтипизацию с использованием протоколов (typing.Protocol). Класс считается подтипом протокола, если он реализует требуемый интерфейс, без необходимости явного наследования[9].

Java

В языке Java отношение is-a между типами одной иерархии классов или интерфейсов выражается с помощью ключевых слов extends и implements. Например, класс ArrayList<E> реализует интерфейс List<E>, а List<E> расширяет Collection<E>. Следовательно, ArrayList<String> является подтипом List<String>, который, в свою очередь, является подтипом Collection<String>. Отношение подтипизации транзитивно. Объявляя интерфейс PayloadList, связывающий с каждым элементом дополнительное значение произвольного типа, его объявление может выглядеть следующим образом:

interface PayloadList<E, P> extends List<E> {
    void setPayload(int index, P val);
    ...
}

Следующие параметризации типа PayloadList являются подтипами типа List<String>:

PayloadList<String, String>
PayloadList<String, Integer>
PayloadList<String, Exception>

В современных версиях Java (начиная с Java 17) появился механизм строгого контроля над иерархией «is-a» с помощью запечатанных (sealed) классов и интерфейсов. В отличие от традиционной модели наследования, они позволяют автору API явно перечислять разрешённые подклассы, используя модификатор sealed и ключевое слово permits. Это создаёт закрытую и заранее известную иерархию, где только указанные типы могут быть прямыми наследниками[10].

Принцип подстановки Лисков

Принцип подстановки Лисков (LSP) формулирует корректность подтипизации следующим образом: «Если для любого объекта o1 типа S существует объект o2 типа T, такой, что во всех программах P, написанных в терминах T, поведение P остаётся неизменным при замене o2 на o1, то S является подтипом T»[11].

Следующий пример иллюстрирует нарушение принципа подстановки Лисков:

class Rectangle
{
  public:
    void   SetWidth(double w)  { itsWidth = w; }
    void   SetHeight(double h) { itsHeight = h; }
    double GetHeight() const   { return itsHeight; }
    double GetWidth() const    { return itsWidth; }
    double GetArea() const     { return GetHeight() * GetWidth(); }
  private:
    double itsWidth;
    double itsHeight;
};

С точки зрения кода, класс Square может быть реализован с наследованием от Rectangle:

public class Square : Rectangle
{
  public:
    virtual void SetWidth(double w);
    virtual void SetHeight(double h);
};
void Square::SetWidth(double w)
{
    Rectangle::SetWidth(w);
    Rectangle::SetHeight(w);
}
void Square::SetHeight(double h)
{
    Rectangle::SetHeight(h);
    Rectangle::SetWidth(h);
}

Однако такая реализация нарушает LSP: несмотря на отношение «is-a» между обобщённым прямоугольником и квадратом, подстановка подкласса «Квадрат» вместо «Прямоугольник» может привести к некорректному поведению.

Рассмотрим, например, следующую функцию, где предположенный результат не выполняется, если передаётся экземпляр класса Square:

void g(Rectangle& r)
{
  r.SetWidth(5);
  r.SetHeight(4);
  assert(r.GetArea() == 20); // утверждение не выполнится!
}

Если же рассматривать ограничение на тип фигуры как на отношение между размерами, то предположения в g() (например, что SetHeight изменяет только высоту, не затрагивая ширину) могут быть неверными не только для настоящих квадратов, но и для других подклассов Rectangle, поведение которых может быть специальным[12].

Концептуальное (таксономическое) отношение «is-a» не всегда гарантирует поведенческую подтипизацию. Принцип подстановки Лисков требует строгого соблюдения контрактов и ожидаемого поведения базового типа[13].

Принцип подстановки Лисков фундаментально применим к семантическим моделям данных и онтологиям (например, OWL). Отношение rdfs:subClassOf требует полного соответствия подкласса семантике надкласса для обеспечения корректности логических выводов.

Применение в современных технологиях

Искусственный интеллект и онтологии

В 2025—2026 годах значительно возросла роль отношения «is-a» в системах искусственного интеллекта. Оно является основой для построения онтологий и машиночитаемых контекстных слоёв (англ. AI-readable context layers), обеспечивая семантическую интероперабельность ИИ-агентов и логический вывод через механизм наследования свойств[14]. В архитектуре гибридных нейросимволических систем иерархии «is-a» служат символическим каркасом, который направляет работу нейронных сетей, повышая их надёжность и объяснимость[15][16].

Базы данных и SQL

В современных реляционных базах данных для реализации семантических и иерархических связей применяются различные подходы. Стандарт SQL:2023 ввёл расширение SQL/PGQ (Property Graph Queries), которое позволяет выполнять графовые запросы поверх существующих реляционных таблиц. Это нововведение предоставляет стандартизированный синтаксис для работы со связями и типами узлов, позволяя эффективно моделировать отношения, близкие к «is-a», непосредственно на уровне запросов[17].[18]

Примечания

  1. Subtypes and Subclasses (англ.). MIT OCW. Дата обращения: 28 мая 2026.
  2. Composition vs Inheritance in OOP. Hejoseph. Дата обращения: 28 мая 2026.
  3. Наследование и композиция в Java. Tproger. Дата обращения: 28 мая 2026.
  4. См. также композиция (объектно-ориентированное программирование).
  5. Puns in OWL: The Whys and Hows. Дата обращения: 28 мая 2026.
  6. std::derived_from. cppreference. Дата обращения: 28 мая 2026.
  7. Reflection in C++26 (P2996). Learn Modern C++ (31 июля 2025). Дата обращения: 28 мая 2026.
  8. Guido van Rossum. Subtyping Built-in Types (англ.). Дата обращения: 28 мая 2026.
  9. PEP 544 -- Protocols: Structural subtyping. Python.org. Дата обращения: 28 мая 2026.
  10. Sealed Classes and Interfaces. Oracle. Дата обращения: 28 мая 2026.
  11. Liskov, Barbara (май 1988). “Data Abstraction and Hierarchy” (PDF). SIGPLAN Notices [англ.]. Архивировано из оригинала (PDF) 2020-06-21. Дата обращения 2026-05-28. Проверьте дату в |date= (справка на английском)
  12. The Liskov Substitution Principle (англ.). Object Mentor. Robert C. Martin, 1996. Дата обращения: 28 мая 2026. Архивировано 5 сентября 2015 года.
  13. Liskov, Barbara and Wing, Jeannette. A Behavioral Notion of Subtyping (1994). Дата обращения: 28 мая 2026.
  14. Enterprise AI Architecture: Inject Business Context into Structured Data with a Semantic Layer. Enterprise Knowledge. Дата обращения: 28 мая 2026.
  15. An Introduction to the Emerging Field of Neuro-Symbolic AI. Ultralytics. Дата обращения: 28 мая 2026.
  16. Нейросимволический ИИ: что будет, если объединить два антагонистических подхода к ИИ? Computerra. Дата обращения: 28 мая 2026.
  17. SQL:2023 Standard. Oralytics (22 августа 2023). Дата обращения: 28 мая 2026.
  18. Новые возможности Postgres Pro. Postgres Pro. Дата обращения: 28 мая 2026.

Литература