JavaとC#では多重継承が許可されていないことを知っています。多くの本では、多重継承は許可されていません。ただし、インターフェイスを使用して実装できます。それが許可されない理由については何も議論されていません。なぜそれが許可されていないのか正確に誰かに教えてもらえますか?
JavaとC#では多重継承が許可されていないことを知っています。多くの本では、多重継承は許可されていません。ただし、インターフェイスを使用して実装できます。それが許可されない理由については何も議論されていません。なぜそれが許可されていないのか正確に誰かに教えてもらえますか?
回答:
短い答えは、言語デザイナーはそうしないことを決めたからです。
基本的に、.NETデザイナーとJavaデザイナーの両方が多重継承を許可していないように思われました。MIを追加すると、言語の複雑さが増しすぎて、メリットがほとんどなくなったためです。
より楽しく、詳細に読むために、言語デザイナーの何人かへのインタビューをウェブ上で利用できるいくつかの記事があります。たとえば、.NETの場合、MSでCLRを担当したChris Brumme氏は、なぜ次のようにしないことにしたのかを説明しています。
言語が異なれば、MIの動作に対する期待も異なります。たとえば、競合がどのように解決されるか、重複するベースがマージされるか冗長であるか。CLRにMIを実装する前に、すべての言語の調査を行い、共通の概念を理解し、言語に依存しない方法でそれらを表現する方法を決定する必要があります。また、MIがCLSに属しているかどうか、およびこの概念を必要としない言語(おそらくVB.NETなど)にとってこれが何を意味するかも決定する必要があります。もちろん、それは私たちが共通言語ランタイムとして使用しているビジネスですが、MIについてはまだ実行していません。
MIが本当に適切な場所の数は実際には非常に少ないです。多くの場合、複数のインターフェースを継承すると、代わりにジョブを実行できます。他の場合では、カプセル化と委任を使用できる場合があります。ミックスインのように少し異なる構成を追加した場合、それは実際により強力になるでしょうか?
複数の実装の継承は、実装に多くの複雑さをもたらします。この複雑さは、キャスト、レイアウト、ディスパッチ、フィールドアクセス、シリアル化、ID比較、検証可能性、リフレクション、ジェネリック、およびおそらく他の多くの場所に影響を与えます。
Javaの場合、次の記事を読むことができます。
Java言語からの多重継承を省略した理由は、主に「単純でオブジェクト指向で使い慣れた」という目標に由来しています。単純な言語として、Javaの作成者は、ほとんどの開発者が大規模なトレーニングなしで理解できる言語を求めていました。そのために、彼らはC ++の不必要な複雑さ(単純)を引き継ぐことなく、言語をC ++にできるだけ似せる(使い慣れた)ように努めました。
設計者の意見では、多重継承はそれが解決するよりも多くの問題と混乱を引き起こします。したがって、それらは言語からの多重継承を削減します(演算子のオーバーロードを削減するのと同じように)。設計者のC ++の豊富な経験から、多重継承は頭痛の種に値しないだけであることがわかりました。
実装の多重継承は許可されていません。
問題は、カウボーイクラスとアーティストクラスがあり、両方にdraw()メソッドの実装がある場合、コンパイラー/ランタイムが何をすべきかを理解できず、新しいCowboyArtistタイプを作成しようとすることです。draw()メソッドを呼び出すとどうなりますか?誰かが通りで死んで横たわっていますか、それとも素敵な水彩画を持っていますか?
それはダブルダイアモンド継承問題と呼ばれていると思います。
理由: Javaはシンプルであるため、非常に人気があり、コーディングが簡単です。
そのため、Java開発者はプログラマにとって理解するのが困難で複雑だと感じたことを避け、それを避けようとしました。そのような種類のプロパティの1つは多重継承です。
多重継承の問題:ダイヤモンドの問題。
例:
これは、ダイヤモンドの問題に存在する曖昧さです。
この問題を解決することは不可能ではありませんが、それを読んでいる間、プログラマーはより多くの混乱と複雑さを生み出します。 解決しようとする以上の問題を引き起こします。
注:ただし、インターフェイスを使用して間接的に常に複数の継承を実装する方法はありません。
Javaの設計哲学はC ++と大きく異なるためです。(ここではC#については説明しません。)
C ++の設計では、Stroustrupは、それらがどのように悪用される可能性があるかに関係なく、有用な機能を含めたかった。多重継承、演算子のオーバーロード、テンプレート、およびその他のさまざまな機能を使用して、時間を浪費することは可能ですが、それらを使用して非常に優れた機能を実行することも可能です。
Javaの設計哲学は、言語構造の安全性を強調することです。その結果、実行するのがはるかに厄介なことがありますが、あなたが見ているコードがあなたが考えていることを意味していることをはるかに確信することができます。
さらに、Javaは、C ++とSmalltalk(最もよく知られているOO言語)からの大部分の反応でした。他のオブジェクト指向言語はたくさんあり(Common Lispは実際には最初に標準化された言語です)、MIをより適切に処理するオブジェクト指向システムがいくつかあります。
言うまでもなく、インターフェース、構成、および委任を使用して、JavaでMIを実行することは完全に可能です。これはC ++よりも明示的であるため、使用するのが不格好ですが、一見して理解しやすいものを入手できます。
ここで正解はありません。答えはさまざまですが、特定の状況にどちらが適しているかは、アプリケーションと個人の好みによって異なります。
人々がMIから離れる主な(決して唯一ではありませんが)理由は、実装のあいまいさをもたらす、いわゆる「ダイヤモンドの問題」です。このウィキペディアの記事はそれについて議論し、私が説明できるよりもよく説明しています。MIはさらに複雑なコードにつながる可能性があり、多くのOO設計者はMIは不要であり、それを使用する場合、モデルはおそらく間違っていると主張します。この最後の点に同意するかどうかはわかりませんが、物事をシンプルに保つことは常に良い計画です。
多重継承は
したがって、Java言語に多重継承を含めないことは、賢明な選択と考えることができます。
昔(70年代)は、コンピュータサイエンスの方が科学で量産が少なかったため、プログラマは優れた設計と優れた実装について考える時間を持っていたため、製品(プログラム)は高品質(TCP / IP設計など)でした。および実装)。今日、誰もがプログラミングしていて、マネージャーが期限前に仕様を変更しているとき、ウィキペディアのリンクにあるSteve Haighの投稿にあるような微妙な問題を追跡することは困難です。したがって、「多重継承」はコンパイラーの設計によって制限されます。あなたがそれを好きなら、あなたはまだC ++を使うことができます...そしてあなたが望むすべての自由を持っています:)
「Javaでは多重継承は許されない」という言葉を少々気にしないでください。
多重継承は、「タイプ」が複数の「タイプ」から継承する場合に定義されます。また、インターフェースには動作があるため、タイプとしても分類されます。したがって、Javaには多重継承があります。それだけが安全です。
クラスを動的にロードすると、多重継承の実装が困難になります。
Javaでは実際に、単一の継承とインターフェースを使用することにより、複数の継承の複雑さを回避しました。以下に説明するような状況では、多重継承の複雑さが非常に高い
多重継承のダイアモンド問題。 Aから継承する2つのクラスBとCがあります。BとCが継承されたメソッドをオーバーライドし、独自の実装を提供するとします。ここで、Dは多重継承を行うBとCの両方から継承します。Dはそのオーバーライドされたメソッドを継承する必要があります。jvmはどのオーバーライドされたメソッドを使用するかを決定できませんか?
c ++では、仮想関数を使用して処理するため、明示的に行う必要があります。
これは、インターフェースを使用することで回避できます。メソッド本体はありません。インターフェイスをインスタンス化することはできません。クラスによって実装するか、他のインターフェイスによって拡張することしかできません。
Javaには、ポリモーフィズムという概念があります。Javaには2種類のポリモーフィズムがあります。メソッドのオーバーロードとメソッドのオーバーライドがあります。その中でも、メソッドのオーバーライドはスーパークラスとサブクラスの関係で発生します。サブクラスのオブジェクトを作成してスーパークラスのメソッドを呼び出す場合、サブクラスが複数のクラスを拡張する場合、どのスーパークラスメソッドを呼び出す必要がありますか?
または、スーパークラスコンストラクターを呼び出しながら super()
、どのスーパークラスコンストラクターが呼び出されますか?
この決定は、現在のJava API機能では不可能です。そのため、Javaでは多重継承は許可されていません。
複数の継承はJavaでは直接許可されていませんが、インターフェースを介して許可されます。
理由:
多重継承:複雑さとあいまいさを導入します。
インターフェース:インターフェースは、Javaの完全に抽象的なクラスであり、プログラムの構造または内部の動作を、一般に利用可能なインターフェースから適切に描くための統一された方法を提供し、その結果、より多くの柔軟性と再利用可能なコードおよびより多くの制御が得られます。他のクラスをどのように作成して操作するかについて。
より正確には、これらはJavaの特別な構成要素であり、一種の多重継承、つまり複数のクラスにアップキャストできるクラスを実行できるようにする追加の特性を備えています。
簡単な例を見てみましょう。
メソッド名は同じで機能が異なる2つのスーパークラスクラスAとBがあるとします。(extends)キーワードを使用した次のコードでは、多重継承はできません。
public class A
{
void display()
{
System.out.println("Hello 'A' ");
}
}
public class B
{
void display()
{
System.out.println("Hello 'B' ");
}
}
public class C extends A, B // which is not possible in java
{
public static void main(String args[])
{
C object = new C();
object.display(); // Here there is confusion,which display() to call, method from A class or B class
}
}
しかし、インターフェースを介して、(implements)キーワードを使用して多重継承が可能です。
interface A
{
// display()
}
interface B
{
//display()
}
class C implements A,B
{
//main()
C object = new C();
(A)object.display(); // call A's display
(B)object.display(); //call B's display
}
}
なぜそれが許可されていないのか正確に誰かに教えてもらえますか?
このドキュメントのリンクから回答を見つけることができます
Javaプログラミング言語で複数のクラスを拡張できない理由の1つは、複数のクラスからフィールドを継承する機能である、状態の多重継承の問題を回避するためです。
多重継承が許可されている場合、そのクラスをインスタンス化してオブジェクトを作成すると、そのオブジェクトはクラスのすべてのスーパークラスからフィールドを継承します。2つの問題が発生します。
異なるスーパークラスのメソッドまたはコンストラクターが同じフィールドをインスタンス化するとどうなりますか?
どのメソッドまたはコンストラクターが優先されますか?
状態の多重継承が許可されていますが、それでも実装できます
タイプの多重継承:クラスが複数のインターフェースを実装する機能。
実装の複数の継承(インターフェースのデフォルトのメソッドによる):複数のクラスからメソッド定義を継承する機能
追加情報については、この関連するSEの質問を参照してください。
C ++では、クラスは複数のクラスから(直接的または間接的に)継承できます。これは多重継承と呼ばれ ますます。ます。
ただし、C#およびJavaでは、クラスが単一継承に限定され、各クラスは単一の親クラスから継承されます。
多重継承は、2つの異なるクラス階層の側面を組み合わせるクラスを作成するための便利な方法です。これは、単一のアプリケーション内で異なるクラスフレームワークを使用するときによく発生します。
たとえば、2つのフレームワークが例外の独自の基本クラスを定義する場合、多重継承を使用して、いずれかのフレームワークで使用できる例外クラスを作成できます。
多重継承の問題は、あいまいになる可能性があることです。古典的な例は、クラスが他の2つのクラスを継承し、それぞれが同じクラスから継承する場合です。
class A {
protected:
bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
public:
void setFlag( bool nflag ){
flag = nflag; // ambiguous
}
};
この例では、flag
データメンバーはによって定義されていclass A
ます。しかしclass D
から下降class B
し、class C
両方の派生から、A
その本質的には、二つのコピーのは、flag
の2つのインスタンスがあるため用意されていA
ているD
のクラス階層。どれを設定しますか?コンパイラは、flag
in への参照D
があいまいであることを報告します。1つの修正は、明示的に参照を明確にすることです。
B::flag = nflag;
別の修正は、BとCを次のように宣言することです。 virtual base classes
することです。つまり、Aのコピーは階層内に1つしか存在できず、あいまいさを排除します。
派生オブジェクトの構築時に基本クラスが初期化される順序や、メンバーが誤って派生クラスから隠される方法など、他の複雑さは複数の継承に存在します。これらの複雑さを回避するために、一部の言語はより単純な単一継承モデルに制限されています。
これは継承をかなり簡略化しますが、共通の祖先を持つクラスのみが動作を共有できるため、その有用性も制限されます。インターフェイスは、異なる階層のクラスが、コードを共有することで実装されていない場合でも、共通のインターフェイスを公開できるようにすることで、この制限をある程度緩和します。
この例を想像してください:私はクラスを持っています Shape1
それにはCalcualteArea
方法があります:
Class Shape1
{
public void CalculateArea()
{
//
}
}
Shape2
同じメソッドを持つ別のクラスがあります
Class Shape2
{
public void CalculateArea()
{
}
}
これでCircleクラスの子クラスができました。これはShape1とShape2の両方から派生しています。
public class Circle: Shape1, Shape2
{
}
ここで、Circleのオブジェクトを作成してメソッドを呼び出しても、システムはどの計算領域メソッドを呼び出すのかわかりません。どちらにも同じ署名があります。そのため、コンパイラは混乱します。そのため、複数の継承は許可されていません。
ただし、インターフェースにはメソッド定義がないため、複数のインターフェースが存在する可能性があります。どちらのインターフェースにも同じメソッドがありますが、どちらにも実装がなく、常に子クラスのメソッドが実行されます。