多重継承は、オブジェクト指向プログラミング言語の一部に見られる機能で、オブジェクトまたはクラスが複数の親オブジェクトまたは親クラスから機能を継承できるものです。これは、オブジェクトまたはクラスが特定の1つのオブジェクトまたはクラスからのみ継承できる単一継承とは異なります。
多重継承は長年にわたり議論の的となっており、[ 1 ] [ 2 ]反対派は、複数の親クラスが特定の機能を実装している場合、どの親クラスからその機能が継承されたのかが曖昧になる「ダイヤモンド問題」のような状況で、多重継承の複雑さと曖昧さが増大することを指摘している。これは、仮想継承の使用など、さまざまな方法で対処できる。[ 3 ]また、曖昧さに対処するために、ミックスインやトレイトなど、継承に基づかないオブジェクト構成の代替方法も提案されている。
オブジェクト指向プログラミング(OOP)において、継承とは、 2つのクラス間の関係を表すもので、一方のクラス(子クラス)が親クラスを継承(または拡張)する関係です。子クラスは親クラスのメソッドと属性を継承するため、機能を共有できます。例えば、食べる、繁殖するなどの機能を持つ変数クラスを作成し、それらの機能を明示的にプログラミングすることなく継承する子クラスを定義し、さらにネズミを追いかけるなどの新しい機能を追加することができます。MammalCat
多重継承により、プログラマーは複数の完全に直交する階層を同時に使用できます。たとえば、クラスCatを拡張したりCartoonCharacter、それらのすべてのクラス内の機能にアクセスしたりすることが可能になります。PetMammal
多重継承をサポートする言語には、C++、Common Lisp(Common Lisp Object System(CLOS)経由)、EuLisp(The EuLisp Object System TELOS経由)、Curl、Dylan、Eiffel、Logtalk、Object REXX、Scala (ミックスインクラスの使用経由)、OCaml、Perl、POP-11、Python、R、Raku、Tcl(8.6以降は組み込み、それ以前のバージョンではIncremental Tcl( Incr Tcl )経由)などがあります[ 4 ] [ 5 ]。
IBMシステムオブジェクトモデル(SOM)ランタイムは多重継承をサポートしており、SOMを対象とするあらゆるプログラミング言語で、複数の基底クラスから継承した新しいSOMクラスを実装できます。
Swift、Java、2003年の改訂以降のFortran、C#、Rubyなどのオブジェクト指向言語の中には、単一継承を実装しているものもありますが、プロトコルやインターフェースによって、真の多重継承の機能の一部が提供されています。
PHPはトレイトクラスを使用して特定のメソッド実装を継承します。Rubyはモジュールを使用して複数のメソッドを継承します。

「ダイヤモンド問題」(「死のダイヤモンド」 [ 6 ]とも呼ばれる)は、2つのクラスBとCがクラスAを継承し、クラスDがBとCの両方を継承する場合に発生する曖昧さです。AにBとCがオーバーライドしたメソッドがあり、Dがそれをオーバーライドしていない場合、Dはどのバージョンのメソッドを継承するのでしょうか?Bのバージョンでしょうか、それともCのバージョンでしょうか?
例えば、GUIソフトウェア開発のコンテキストでは、クラスは(外観のため)と(機能/入力処理のため)Buttonの両方のクラスから継承し、クラスとクラスは両方ともクラスから継承します。ここで、オブジェクトに対してメソッドが呼び出され、クラスにそのようなメソッドがないが、または(あるいは両方)にオーバーライドされたメソッドがある場合、最終的にどちらのメソッドが呼び出されるべきでしょうか?RectangleClickableRectangleClickableObjectequalsButtonButtonequalsRectangleClickable
この状況におけるクラス継承図の形状から、「ダイヤモンド問題」と呼ばれています。この場合、クラスAが最上部にあり、その下にBとCがそれぞれ別々に配置され、最下部でDが両者を結合してダイヤモンド型を形成します。
言語によって、こうした継承の繰り返しという問題に対処する方法は異なる。
Aインターフェイスを実装しIa、Ib同様のメソッドにデフォルト実装を持つクラスでは、同じシグネチャを持つ 2 つの「継承」メソッドを持つことになり、ダイヤモンド問題が発生します。この問題はA、メソッド自体を実装することを要求して曖昧さをなくすか、呼び出し元にAオブジェクトを適切なインターフェイスにキャストさせてそのメソッドのデフォルト実装を使用させる (例: ((Ia) aInstance).Method();) ことで軽減されます。Dオブジェクトには実際には 2 つの別々のAオブジェクトが含まれることになり、のメンバーの使用は適切に修飾する必要があります。からへAの継承とからへの継承の両方が " " (たとえば " ") とマークされている場合、C ++は1 つのオブジェクトのみを作成するように特別に注意し、のメンバーの使用は正しく機能します。仮想継承と非仮想継承が混在している場合、仮想の が 1 つと、への各非仮想継承パスに対して非仮想の が 1 つ存在します。C++ では、使用する機能がどの親クラスから呼び出されるかを明示的に指定する必要があります。つまり、です。C++ は、どのスーパークラスを使用するかを修飾する方法がないため (つまり、クラスが単一の派生リスト [ ] に複数回出現する)、明示的な繰り返し継承をサポートしていません。C++ では、仮想継承メカニズムを介して複数のクラスの単一のインスタンスを作成することもできます (つまり、とが同じオブジェクトを参照します)。ABACvirtualclass B : virtual public AAAAAAWorker::Human.ageclass Dog : public Animal, AnimalWorker::HumanMusician::HumanD,B,C,Aクラス定義で B が C より前に記述されている場合、メソッドは でソートされます。最も具体的な引数クラスを持つメソッドが選択され (D>(B,C)>A) 、次にサブクラス定義で親クラスが命名されている順序 (B>C) で選択されます。ただし、プログラマは、特定のメソッド解決順序を指定したり、メソッドの組み合わせに関するルールを宣言したりすることで、これをオーバーライドできます。これはメソッドの組み合わせと呼ばれ、完全に制御できます。MOP (メタオブジェクトプロトコル) は、システムの安定性に影響を与えることなく、継承、動的ディスパッチ、クラスインスタンス化、およびその他の内部メカニズムを変更する手段も提供します。D2 つの構造体 と を埋め込みB、C両方の構造体がメソッド を持ちF()、インターフェース を満たす場合、 が呼び出されたとき、または のインスタンスが型の変数に代入されたときに、Aコンパイラは「あいまいなセレクタ」について警告します。とのメソッドは、または で明示的に呼び出すことができます。D.F()DABCD.B.F()D.C.F()A、BおよびCがインターフェースである場合、Bおよび はそれぞれ の抽象メソッドに対してC異なる実装を提供できるため、ダイヤモンド問題が発生します。 どちらのクラスもメソッドを再実装する必要があります ( の本体は、呼び出しをスーパークラスの実装のいずれかに転送するだけで済みます)。そうでない場合、曖昧さはコンパイル エラーとして拒否されます。[ 8 ] Java 8 より前は、インターフェースのデフォルト メソッドが利用できなかったため、Java はダイヤモンド問題のリスクにさらされていませんでした。現在の最新バージョン 26 までのすべての Java バージョンは、多重継承を直接サポートしていません。クラスは単一のクラスのみを継承できますが、デフォルト メソッドを持つ複数のインターフェースを実装できます。AD(individual as Person).printInfo();。例: super<ChosenParentInterface>.someMethod()Bとその祖先がクラスとその祖先よりも先にチェックされるCため、のメソッドはをA介して継承されます。これはIoおよびPicolispBと共通です。Perl では、この動作は、または他のモジュールを使用してC3 線形化または他のアルゴリズムを使用することでオーバーライドできます。[ 9 ]mroobject(またはメソッド解決順序 (MRO)) アルゴリズムを使用してクラスのリストを作成します。このアルゴリズムは、2 つの制約を強制します。子は親より先に存在し、クラスが複数のクラスから継承している場合は、基底クラスのタプルで指定された順序で保持されます (ただし、この場合、継承グラフの上位にある一部のクラスは、グラフの下位にあるクラスより先に存在することができます[ 10 ] )。したがって、メソッド解決順序は、、、、となります[ 11 ]DBCAD, C, A, B, ] であり、これは [ , , , ]Aに縮小されます。DCBAクラスが 1 つの基底クラスからのみ派生できる単一継承のみを許可する言語には、ダイヤモンド問題はありません。その理由は、このような言語では、メソッドの繰り返しや配置に関係なく、継承チェーンのどのレベルでもメソッドの実装が最大 1 つしかないためです。通常、これらの言語では、クラスが複数のプロトコルを実装できます。Java では、インターフェースと呼ばれます。これらのプロトコルはメソッドを定義しますが、具体的な実装は提供しません。この戦略は、 ActionScript、C#、D、Java、Nemerle、Object Pascal、Objective-C、Smalltalk、Swift、PHPで使用されています。[ 13 ]これらの言語はすべて、クラスが複数のプロトコルを実装することを許可しています。
さらに、Ada、C#、Java、Object Pascal、Objective-C、Swift、PHPでは、インターフェース(Objective-CとSwiftではプロトコルと呼ばれる)の多重継承が可能です。インターフェースは、動作を実装せずにメソッドシグネチャを指定する抽象基底クラスのようなものです。(Javaバージョン7までの「純粋な」インターフェースでは、インターフェースに実装やインスタンスデータを含めることはできません。)ただし、複数のインターフェースが同じメソッドシグネチャを宣言している場合でも、継承チェーンのどこかでそのメソッドが実装(定義)されると、その上位のチェーン(スーパークラス)にあるそのメソッドの実装はすべて上書きされます。したがって、継承チェーンのどのレベルでも、メソッドの実装は最大で1つしか存在できません。そのため、単一継承のメソッド実装では、インターフェースの多重継承があっても、ダイヤモンド問題は発生しません。Java 8とC# 8でインターフェースのデフォルト実装が導入されたことで、ダイヤモンド問題が発生する可能性は依然としてありますが、これはコンパイル時エラーとしてのみ表示されます。