В чем разница между подтипированием и наследованием в программировании OO?

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

В чем разница между подтипированием и наследованием в объектно-ориентированном программировании?

Ответ 1

В дополнение к уже даным ответам, здесь ссылка на статью, я думаю, является актуальной. Выдержки:

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

  • Подтип относится к совместимости интерфейсов. Тип B является подтипом A если каждая функция, которая может быть вызвана для объекта типа A также может быть вызвана для объекта типа B
  • Наследование относится к повторному использованию реализаций. Тип B наследуется от другого типа A если некоторые функции для B написаны в терминах функций из A

Однако подтипирование и наследование не должны идти рука об руку. Рассмотрим структуру данных deque, двустороннюю очередь. Deque поддерживает вставку и удаление на обоих концах, поэтому он имеет четыре функции: insert-front, delete-front, insert-rear и delete-rear. Если мы используем просто insert-rear и delete-front мы получим обычную очередь. С другой стороны, если мы используем просто insert-front и delete-front, мы получаем стек. Другими словами, мы можем реализовать очереди и стеки в терминах deques, так что типы данных, Stack и Queue наследуются от Deque. С другой стороны, ни Stack ни Queue являются подтипами Deque поскольку они не поддерживают все функции, предоставляемые Deque. Фактически, в этом случае Deque является подтипом и Stack и Queue !

Я думаю, что Java, C++, С# и им подобные способствовали путанице, как уже отмечалось, тем фактом, что они объединяют обе идеи в единую иерархию классов. Тем не менее, я думаю, что приведенный выше пример отдает должное идеям довольно не зависящим от языка образом. Я уверен, что другие могут привести больше примеров.

Ответ 2

Относительно, к сожалению, умер и оставил вам свой книжный магазин.

Теперь вы можете читать все книги, продавать их, просматривать свои учетные записи, список своих клиентов и т.д. Это наследование - у вас есть все, что имеет родственник. Наследование - это форма повторного использования кода.

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

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

В языках, перечисленных в вашем вопросе - С++, Java и С# - эти два (почти) всегда используются вместе, и, таким образом, единственный способ унаследовать от чего-либо - это подтип его и наоборот. Но другие языки не обязательно сливают эти два понятия.

Ответ 3

Наследование - это получение атрибутов (и/или функциональных возможностей) супер-типов. Например:

class Base {
    //interface with included definitions

}

class Derived inherits Base {
    //Add some additional functionality.
    //Reuse Base without having to explicitly forward
    //the functions in Base
}

Здесь Derived не может использоваться там, где ожидается Base, но он может действовать аналогично Base, добавляя поведение или изменяя некоторый аспект поведения Base. Как правило, Base будет небольшим вспомогательным классом, который обеспечивает как интерфейс, так и реализацию для некоторых обычно требуемых функций.

Подтип-полиморфизм связан с реализацией интерфейса и тем самым может заменить различные реализации этого интерфейса во время выполнения:

class Interface {
    //some abstract interface, no definitions included
}

class Implementation implements Interface {
    //provide all the operations 
    //required by the interface
}

Здесь Implementation можно использовать везде, где требуется Interface, и во время выполнения могут быть заменены различные реализации. Цель состоит в том, чтобы более широко использовать код, который использует Interface.

Ваше замешательство оправдано. Java, С# и С++ объединяют эти две идеи в одну иерархию классов. Однако эти два понятия не идентичны, и существуют языки, которые разделяют два.

Ответ 4

Если вы наследуете конфиденциально в C++, вы получаете наследование без подтипов. То есть дано:

class Derived : Base        // note the missing public before Base

Вы не можете написать:

Base * p = new Derived();   // type error

Потому что Derived не является подтипом Base. Вы просто унаследовали реализацию, а не тип.

Ответ 5

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