web-dev-qa-db-ja.com

OOプログラミングにおけるサブタイプと継承の違いは何ですか?

主な違いは見つかりませんでした。そして、いつ継承を使用できるのか、いつサブタイプを使用できるのか、私は非常に混乱しています。私はいくつかの定義を見つけましたが、それらはあまり明確ではありません。

オブジェクト指向プログラミングにおけるサブタイピングと継承の違いは何ですか?

19
Micha agus

すでに与えられた答えに加えて、ここに私が関連すると思う記事への リンク があります。抜粋:

オブジェクト指向フレームワークでは、継承は通常、クラスの階層で抽象データ型を編成するときにサブタイプと連携する機能として提示されます。ただし、この2つは直交する考えです。

  • サブタイピングとは、インターフェースの互換性を指します。タイプBは、タイプAのオブジェクトで呼び出すことができるすべての関数が、タイプAのオブジェクトでも呼び出すことができる場合、Bのサブタイプです。
  • 継承とは、実装の再利用を指します。 Bの一部の関数がAの関数に関して記述されている場合、タイプBは別のタイプAを継承します。

ただし、サブタイピングと継承は密接に関連している必要はありません。データ構造deque、両端キューについて考えてみます。 dequeは両端での挿入と削除をサポートしているため、insert-frontdelete-frontinsert-rear、およびdelete-rearの4つの関数があります。 insert-reardelete-frontだけを使用すると、通常のキューが作成されます。一方、insert-frontdelete-frontだけを使用すると、スタックが取得されます。言い換えると、データ型StackQueueDequeから継承するように、両端キューの観点からキューとスタックを実装できます。一方、StackQueueも、Dequeが提供するすべての機能をサポートしているわけではないため、Dequeのサブタイプではありません。実際、この場合、DequeStackQueueの両方のサブタイプです!

すでに述べたように、Java、C++、C#、およびそれらの同類は、両方のアイデアを単一のクラス階層に統合するという事実によって、混乱の一因となったと思います。ただし、上記の例は、言語に依存しない方法でアイデアを正当化すると思います。他の人がもっと例をあげることができると確信しています。

25
adino

残念ながら、親戚が亡くなり、あなたに彼の本屋を残しました。

これで、そこですべての本を読んだり、販売したり、彼のアカウントや顧客リストなどを確認したりできます。これは継承-親戚が持っていたすべてのものを持っています。継承は、コードの再利用の一形態です。

自分で本屋を再開して、親戚の役割と責任をすべて引き受けることもできます。自分で変更を加えても、これはサブタイプです。あなたは本屋のオーナーになりました。あなたの親戚がかつてあったように。

サブタイピングはOOPの重要なコンポーネントです。あるタイプのオブジェクトがありますが、別のタイプのインターフェイスを満たしているため、他のオブジェクトを使用できた場所ならどこでも使用できます。

質問にリストした言語(C++、Java、C#))では、この2つは(ほとんど)常に一緒に使用されるため、何かから継承する唯一の方法は、サブタイプ化することです。 。しかし、他の言語は必ずしも2つの概念を融合するとは限りません。

10
Oak

継承とは、スーパータイプの属性(および/または機能)を取得することです。例えば:

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が期待される場所では使用できませんが、動作を追加したり、Basesの動作の一部を変更したりしながら、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つを分離する言語が存在します。

6
Mankarse

C++でプライベートに継承する場合、サブタイプなしで継承を取得します。つまり、与えられた:

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

あなたは書くことができません:

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

DerivedBaseのサブタイプではないためです。タイプではなく、実装を継承しただけです。

5
fredoverflow

簡単な言葉で:サブタイプと継承は両方ともポリモーフィズムです(継承は動的ポリモーフィズムです-オーバーライドします)。実際には、継承はサブクラス化です。つまり、継承では、スーパークラスでサブクラスの機能を保証する保証はありません(サブクラスがスーパークラスの動作を破棄しないようにしてください)が、サブタイピング(インターフェイスの実装など)では、クラスは期待される動作を破棄しません。

サブタイピングは、継承を介して実装する必要はありません。継承ではないいくつかのサブタイプ:

  1. Ocamlの変種
  2. Rustの生涯注釈
  3. Cleanの一意性タイプ
  4. Goのインターフェース
1
Akangka