web-dev-qa-db-ja.com

サブクラスとサブタイプの違いは何ですか?

Liskov置換原理について この質問 に対する最高評価の回答は、用語subtypeサブクラス。また、一部の言語は2つを混同するが、他の言語は混同しないことも重要です。

私が最もよく知っているオブジェクト指向言語(Python、C++)では、「タイプ」と「クラス」は同義の概念です。 C++に関して、サブタイプとサブクラスを区別するとはどういう意味ですか?たとえば、FooFooBaseのサブクラスではなく、サブタイプであるとします。 fooFooのインスタンスである場合、次の行になります。

FooBase* fbPoint = &foo;

もう有効ではありませんか?

45
tel

Subtypingは型多型の形式であり、サブタイプは、代替可能性の概念によって別のデータ型(スーパータイプ)に関連するデータ型です。スーパータイプの要素を操作するように記述されたプログラム要素(通常はサブルーチンまたは関数)も、サブタイプの要素を操作できます。

STのサブタイプである場合、サブタイプ関係はしばしばS <: Tは、タイプSの用語が期待されるコンテキストで、タイプTの用語を安全に使用できることを意味します。サブタイピングの正確なセマンティクスは、特定のプログラミング言語で「どこで安全に使用されるか」が何を意味するかの詳細に大きく依存します。

サブクラス化をサブタイプと混同しないでください。一般に、サブタイプ化はis-a関係を確立しますが、サブクラス化は実装を再利用し、構文関係を確立するだけで、必ずしも意味関係を確立しません(継承は動作のサブタイプを保証しません)。

これらの概念を区別するために、サブタイピングはインターフェイス継承とも呼ばれ、サブクラス化は実装継承またはコード継承とも呼ばれます。

参照
サブタイプ
継承

53
Robert Harvey

ここで説明しているコンテキストでのtypeは、本質的に一連の動作保証です。 A contract(必要な場合)。または、Smalltalkから用語を借りて、a protocol

classはメソッドのバンドルです。 動作の実装のセットです。

サブタイピングは、プロトコルを改良する手段です。 サブクラス化は、動作の違いを説明するだけでコードを再利用する、つまりコードを再利用する手段です。

JavaまたはC♯を使用した場合、すべての型はinterface型である必要があるというアドバイスに出くわすかもしれません。実際、William CookのOnデータ抽象化について、再考、そうすればOOこれらの言語で行うにはmust型としてinterfacesのみを使用することを知っているかもしれません。(また、面白い事実: Java Objective-Cのプロトコルからinterfacesを直接cribbedし、Smalltalkから直接取得しています。)

次に、そのコーディングのアドバイスに従って論理的な結論を導き、Javaのバージョンを想像してみます。ここで、-onlyinterfacesはタイプであり、クラスとプリミティブはそうではありません。一方、別のinterfaceを継承するclassは、superによる差分コードの再利用のみを目的としています。

私が知る限り、継承code(実装の継承/サブクラス化)と継承contracts(サブタイピング)を厳密に区別する主流の静的型付き言語はありません。 JavaおよびC♯では、インターフェイスの継承は純粋なサブタイプです(または少なくとも、Java 8でデフォルトのメソッドが導入されるまでは、しかし、クラスの継承はalsoサブタイプ化と実装の継承です。mixinsを厳密に区別する、実験的に静的に型付けされたオブジェクト指向のLISP方言について読んだことを覚えています。 contain動作)、structs(状態を含む)、interfaces(これはdescribe動作)、およびクラス(1つ以上のミックスインで0個以上の構造体を構成し、1つ以上のインターフェイスに準拠します。)インスタンス化できるのはクラスだけで、型として使用できるのはインターフェイスだけです。

動的に型付けされるOO Python、Ruby、ECMAScript、Smalltalkなどの言語では、通常、オブジェクトの型は、それが準拠するプロトコルのセットと見なされます。複数形:オブジェクトは複数のタイプを持つことができ、タイプStringの各オブジェクトはタイプObjectのオブジェクトでもあるということだけではありません(BTW:クラス名を使用してタイプについて話す方法に注意してください。 me!)オブジェクトは複数のプロトコルを実装できます。たとえば、Rubyでは、Arraysを追加したり、インデックスを付けたり、反復したり、比較したりできます。これは、4つの異なるプロトコルで実装されています!

Rubyには型がありません。しかしRuby communityには型があります!それらはプログラマの頭にだけ存在します、ただし、ドキュメントでは、たとえば、要素を1つずつ生成することによってeachというメソッドに応答するオブジェクトはenumerableオブジェクトと見なされます。また、Enumerableというミックスインがあり、 dependsこのプロトコル。したがって、オブジェクトに正しいtype(プログラマーの頭にのみ存在)がある場合、Enumerable mixinをミックスイン(継承)できます、そしてmapreducefilterなどのあらゆる種類のクールなメソッドを無料で手に入れることができます。

同様に、オブジェクトが_<=>_に応答する場合、オブジェクトはcomparableプロトコルを実装していると見なされ、Comparableミックスインで混合して_<_、_<=_、_>_、_<=_、_==_、_between?_、clampは無料です。ただし、これらのメソッド自体をすべて実装することもでき、Comparableをまったく継承しないため、比較可能と見なされます。

良い例は、StringIOライブラリです。これは、基本的にfakes文字列を含むI/Oストリームです。 IOクラスとすべて同じメソッドを実装しますが、2つの間に継承関係はありません。それでも、StringIOは、IOを使用できるすべての場所で使用できます。これは単体テストで非常に役立ちます。プログラムをさらに変更することなく、ファイルまたはstdinStringIOに置き換えることができます。 StringIOは、IOと同じプロトコルに準拠しているため、クラスが異なっていても、両方とも同じ型であり、関係を共有していません(どちらかがObjectを拡張するという些細なことを除いて)。

28
Jörg W Mittag