主な違いは見つかりませんでした。そして、いつ継承を使用できるのか、いつサブタイプを使用できるのか、私は非常に混乱しています。私はいくつかの定義を見つけましたが、それらはあまり明確ではありません。
オブジェクト指向プログラミングにおけるサブタイピングと継承の違いは何ですか?
すでに与えられた答えに加えて、ここに私が関連すると思う記事への リンク があります。抜粋:
オブジェクト指向フレームワークでは、継承は通常、クラスの階層で抽象データ型を編成するときにサブタイプと連携する機能として提示されます。ただし、この2つは直交する考えです。
- サブタイピングとは、インターフェースの互換性を指します。タイプ
B
は、タイプA
のオブジェクトで呼び出すことができるすべての関数が、タイプA
のオブジェクトでも呼び出すことができる場合、B
のサブタイプです。- 継承とは、実装の再利用を指します。
B
の一部の関数がA
の関数に関して記述されている場合、タイプB
は別のタイプA
を継承します。ただし、サブタイピングと継承は密接に関連している必要はありません。データ構造deque、両端キューについて考えてみます。 dequeは両端での挿入と削除をサポートしているため、
insert-front
、delete-front
、insert-rear
、およびdelete-rear
の4つの関数があります。insert-rear
とdelete-front
だけを使用すると、通常のキューが作成されます。一方、insert-front
とdelete-front
だけを使用すると、スタックが取得されます。言い換えると、データ型Stack
とQueue
がDeque
から継承するように、両端キューの観点からキューとスタックを実装できます。一方、Stack
もQueue
も、Deque
が提供するすべての機能をサポートしているわけではないため、Deque
のサブタイプではありません。実際、この場合、Deque
はStack
とQueue
の両方のサブタイプです!
すでに述べたように、Java、C++、C#、およびそれらの同類は、両方のアイデアを単一のクラス階層に統合するという事実によって、混乱の一因となったと思います。ただし、上記の例は、言語に依存しない方法でアイデアを正当化すると思います。他の人がもっと例をあげることができると確信しています。
残念ながら、親戚が亡くなり、あなたに彼の本屋を残しました。
これで、そこですべての本を読んだり、販売したり、彼のアカウントや顧客リストなどを確認したりできます。これは継承-親戚が持っていたすべてのものを持っています。継承は、コードの再利用の一形態です。
自分で本屋を再開して、親戚の役割と責任をすべて引き受けることもできます。自分で変更を加えても、これはサブタイプです。あなたは本屋のオーナーになりました。あなたの親戚がかつてあったように。
サブタイピングはOOPの重要なコンポーネントです。あるタイプのオブジェクトがありますが、別のタイプのインターフェイスを満たしているため、他のオブジェクトを使用できた場所ならどこでも使用できます。
質問にリストした言語(C++、Java、C#))では、この2つは(ほとんど)常に一緒に使用されるため、何かから継承する唯一の方法は、サブタイプ化することです。 。しかし、他の言語は必ずしも2つの概念を融合するとは限りません。
継承とは、スーパータイプの属性(および/または機能)を取得することです。例えば:
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
sの動作の一部を変更したりしながら、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、C#、およびC++はすべて、これら2つのアイデアを単一のクラス階層に統合します。ただし、2つの概念は同一ではなく、2つを分離する言語が存在します。
C++でプライベートに継承する場合、サブタイプなしで継承を取得します。つまり、与えられた:
class Derived : Base // note the missing public before Base
あなたは書くことができません:
Base * p = new Derived(); // type error
Derived
はBase
のサブタイプではないためです。タイプではなく、実装を継承しただけです。
簡単な言葉で:サブタイプと継承は両方ともポリモーフィズムです(継承は動的ポリモーフィズムです-オーバーライドします)。実際には、継承はサブクラス化です。つまり、継承では、スーパークラスでサブクラスの機能を保証する保証はありません(サブクラスがスーパークラスの動作を破棄しないようにしてください)が、サブタイピング(インターフェイスの実装など)では、クラスは期待される動作を破棄しません。
サブタイピングは、継承を介して実装する必要はありません。継承ではないいくつかのサブタイプ: