Javaでは多重継承は許可されていませんが、複数のインターフェースを実装できます。どうして?
Javaでは多重継承は許可されていませんが、複数のインターフェースを実装できます。どうして?
回答:
インタフェースのみを指定しているので何クラスがない、やっているどのようにそれをやっています。
多重継承の問題は、2つのクラスが同じことを行う異なる方法を定義する可能性があり、サブクラスがどちらを選択するかを選択できないことです。
大学の講師の一人がこのように説明してくれました:
トースターというクラスと、NuclearBombというクラスがあるとします。彼らは両方とも「暗闇」の設定を持っているかもしれません。どちらにもon()メソッドがあります。(1つにはoff()があり、もう1つにはありません。)これらの両方のサブクラスであるクラスを作成したい場合は、ご覧のように、これは私の顔で本当に爆発する可能性がある問題です。
したがって、主な問題の1つは、2つの親クラスがある場合、同じ機能の実装が異なる可能性があることです。あるいは、私のインストラクターの例のように、同じ名前の2つの異なる機能がある可能性があります。次に、サブクラスで使用するものを決定する必要があります。これを処理する方法は確かにあります— C ++はそうします—しかし、Javaの設計者は、これにより事態が複雑になりすぎると感じました。
ただし、インターフェイスを使用すると、別のクラスの処理方法を借用するのではなく、クラスが実行できる処理を記述します。複数のインターフェースは、複数の親クラスよりも、解決が必要なトリッキーな競合を引き起こす可能性がはるかに低くなります。
「そのメソッドは便利に見えるので、そのクラスも拡張します」とは言えない場合でも、継承が過剰に使用されるためです。
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
この質問の答えは、Javaコンパイラの内部動作(コンストラクタチェーン)にあります。 Javaコンパイラーの内部動作を確認する場合:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
public void printBankBalance(){
System.out.println("20k");
}
}
これをコンパイルすると、次のようになります。
public class Bank {
public Bank(){
super();
}
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank {
SBI(){
super();
}
public void printBankBalance(){
System.out.println("20k");
}
}
クラスを拡張してそのオブジェクトを作成すると、1つのコンストラクタチェーンがObject
クラスまで実行されます。
上記のコードは正常に実行されます。しかし、Car
拡張Bank
と呼ばれる別のクラスがあり、1つのハイブリッド(多重継承)クラスと呼ばれる場合SBICar
:
class Car extends Bank {
Car() {
super();
}
public void run(){
System.out.println("99Km/h");
}
}
class SBICar extends Bank, Car {
SBICar() {
super(); //NOTE: compile time ambiguity.
}
public void run() {
System.out.println("99Km/h");
}
public void printBankBalance(){
System.out.println("20k");
}
}
この場合、(SBICar)はコンストラクタチェーン(コンパイル時のあいまいさ)の作成に失敗します。
インターフェースの場合、オブジェクトを作成できないため、これは許可されます。
default
とstatic
メソッドの新しい概念については、インターフェイスのデフォルトを参照してください。
これでクエリが解決することを願っています。ありがとう。
複数のインターフェースを実装することは非常に便利であり、言語の実装者やプログラマーに多くの問題を引き起こしません。許可されています。多重継承も有用ですが、ユーザーに深刻な問題を引き起こす可能性があります(恐ろしい死のダイヤモンド)。また、多重継承で行うほとんどのことは、構成または内部クラスを使用して行うこともできます。したがって、複数の継承は、利益よりも多くの問題をもたらすため禁止されています。
ToyotaCar
とHybridCar
の両方を由来Car
とオーバーライドCar.Drive
、および場合PriusCar
継承の両方が、上書きしていないDrive
、システムが仮想のものを特定する手立てもないだろうCar.Drive
行う必要があります。インターフェイスは、上記のイタリック体の条件を回避することにより、この問題を回避します。
void UseCar(Car &foo)
; ToyotaCar::Drive
とのあいまいさをなくすことは期待できませんHybridCar::Drive
(多くの場合、他のタイプが存在することさえ知らないか、気にする必要がないためです)。言語は、C ++と同様に、コードToyotaCar &myCar
を渡したい場合、UseCar
最初にHybridCar
またはToyotaCar
にキャストする必要がありますが、((Car)(HybridCar)myCar).Drive`以降、((Car)(ToyotaCar)myCar).Drive
さまざまなことを行うため、アップキャストを意味しますアイデンティティを保持していませんでした。
このクエリの正確な答えは、多重継承に関するOracleのドキュメントページにあります。
状態の多重継承: 複数のクラスからフィールドを継承する機能
Javaプログラミング言語で複数のクラスを拡張できない理由の1つは、複数のクラスからフィールドを継承する機能である、状態の多重継承の問題を回避するためです。
多重継承が許可されている場合、そのクラスをインスタンス化してオブジェクトを作成すると、そのオブジェクトはクラスのすべてのスーパークラスからフィールドを継承します。2つの問題が発生します。
実装の多重継承:複数のクラスからメソッド定義を継承する機能
このアプローチの問題:名前の競合とあいまいさ。サブクラスとスーパークラスに同じメソッド名(およびシグニチャー)が含まれている場合、コンパイラーは呼び出すバージョンを判別できません。
ただし、Javaは、Java 8のリリース以降に導入されたデフォルトのメソッドでこのタイプの多重継承をサポートしています。Javaコンパイラは、特定のクラスが使用するデフォルトのメソッドを決定するためのいくつかのルールを提供します。
ダイヤモンドの問題の解決の詳細については、以下のSEの投稿を参照してください。
タイプの多重継承:複数の インターフェースを実装するクラスの機能。
インターフェースには変更可能なフィールドが含まれていないため、ここでの状態の多重継承に起因する問題について心配する必要はありません。
オブジェクトの状態はそのフィールドに関して参照されると言われており、あまりに多くのクラスが継承された場合、あいまいになるでしょう。こちらがリンクです
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Javaは、インターフェースのみによる多重継承をサポートしています。クラスは任意の数のインターフェースを実装できますが、拡張できるクラスは1つだけです。
多重継承は致命的なダイヤモンドの問題につながるため、サポートされていません。ただし、解決することはできますが、システムが複雑になるため、Javaの創設者によって多重継承が削除されました。
1995年2月のJames Goslingによる「Java:an Overview」というタイトルのホワイトペーパーで、リンクでJavaで多重継承がサポートされない理由について説明しています。
ゴスリングによると:
「JAVAは、C ++のめったに使用されず、あまり理解されておらず、混乱している機能の多くを省略しています。C++の経験では、利益よりも悲しみをもたらします。これは、主に演算子のオーバーロード(メソッドのオーバーロードはあります)、多重継承、および広範な自動強制で構成されています。」
同じ理由で、C#は複数の継承を許可しませんが、複数のインターフェイスを実装できます。
多重継承を伴うC ++から学んだ教訓は、それが価値よりも多くの問題を引き起こすということでした。
インターフェイスは、クラスが実装する必要があるもののコントラクトです。インターフェースから機能を得ることができません。継承により、親クラスの機能を継承できます(多重継承では、非常に混乱する可能性があります)。
複数のインターフェイスを許可すると、デザインパターン(アダプターなど)を使用して、複数の継承を使用して解決できるのと同じタイプの問題を、より信頼性が高く予測可能な方法で解決できます。
D1
とD2
の両方から継承B
し、各オーバーライド機能がf
あれば、とobj
タイプのインスタンスであるS
両方から継承D1
やD2
けどをオーバーライドしていないがf
、その後するための基準キャストS
するD1
何か得られるはずf
の用途D1
オーバーライドを、とにキャストB
すべきでありませんそれを変えなさい。同様に、参照S
をキャストすると、オーバーライドD2
をf
使用するものが生成され、それを変更しD2
てB
はなりません。言語が仮想メンバーの追加を許可する必要がない場合
このトピックは近いものではないので、この回答を投稿します。Javaが多重継承を許可しない理由を誰かが理解するのに役立つことを願っています。
次のクラスを考えてみましょう:
public class Abc{
public void doSomething(){
}
}
この場合、Abcクラスは何も拡張しませんか?それほど高速ではないため、このクラスは暗黙的にクラスObjectを拡張し、すべてがJavaで機能できるようにする基本クラスです。すべてがオブジェクトです。
あなたがあなたのIDEは、あなたのような方法で使用できるようにすることがわかります上記のクラスを使用しようとすると:equals(Object o)
、toString()
、などがありますが、それらのメソッドを宣言していない、彼らは基本クラスから来ましたObject
あなたは試すことができます:
public class Abc extends String{
public void doSomething(){
}
}
あなたのクラスは暗黙の拡張ではObject
なくString
、あなたが言ったので拡張するので、これは問題ありません。次の変更を検討してください。
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
これで、toString()を呼び出すと、クラスは常に「hello」を返します。
次のクラスを想像してみてください。
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
ここでも、クラスFlyer
の暗黙的なメソッドを持つオブジェクトを拡張しtoString()
、それらのすべてが延びているので、任意のクラスは、このメソッドを持っていますObject
、あなたが呼び出す場合、そう、間接的toString()
にBird
その、toString()
Javaが使用する必要がありますでしょうか?からAbc
またはFlyer
?これは、2つ以上のクラスを拡張しようとするすべてのクラスで発生します。この種の「メソッドの衝突」を回避するために、インターフェイスの概念を構築しました。基本的に、それらはObjectを間接的に拡張しない抽象クラスと考えることができます。それらは抽象的であるため、オブジェクトであるクラスによって実装する必要があります (インターフェースだけをインスタンス化することはできません。それらはクラスによって実装される必要があります)。そのため、すべてが正常に機能し続けます。
インターフェースとクラスを区別するために、キーワードimplementsはインターフェース専用に予約されています。
デフォルトでは何も拡張しないため、同じクラスに好きなインターフェースを実装できます(ただし、別のインターフェースを拡張するインターフェースを作成することはできますが、「father」インターフェースはObjectを拡張しません)。したがって、インターフェースは単なるインターフェイスであり、「メソッドシグネチャコリジョン」の影響を受けません。コンパイラが警告をスローし、メソッドシグネチャを変更して修正する必要があります(signature = method name + params + return type) 。
public interface Flyer{
public void makeFly(); // <- method without implementation
}
public class Bird extends Abc implements Flyer{
public void doAnotherThing(){
}
@Override
public void makeFly(){ // <- implementation of Flyer interface
}
// Flyer does not have toString() method or any method from class Object,
// no method signature collision will happen here
}
インターフェースは単なる契約だからです。そしてクラスは実際にはデータのコンテナです。
たとえば、同じメソッドm1()を持つ2つのクラスA、B。また、クラスCはA、Bの両方を拡張します。
class C extends A, B // for explaining purpose.
これで、クラスCはm1の定義を検索します。まず、見つからなかった場合はクラスを検索し、親クラスをチェックします。AとBの両方が定義を持っているので、ここでどちらの定義を選択すべきかが曖昧になります。したがって、JAVAは複数の継承をサポートしていません。
次の2つの理由により、Javaは多重継承をサポートしていません。
Object
。複数のスーパークラスから継承する場合、サブクラスはオブジェクトクラスのプロパティを取得するというあいまいさを取得します。super()
、夕食クラスコンストラクターを呼び出すための呼び出しです。クラスに複数のスーパークラスがある場合、混乱します。したがって、1つのクラスが複数のスーパークラスから拡張されると、コンパイル時エラーが発生します。
たとえば、クラスAにgetSomethingメソッドがあり、クラスBにgetSomethingメソッドがあり、クラスCがAとBを拡張している場合を考えてみます。誰かがC.getSomethingを呼び出すとどうなりますか?呼び出すメソッドを決定する方法はありません。
インターフェイスは基本的に、実装するクラスに含める必要があるメソッドを指定するだけです。複数のインターフェースを実装するクラスは、そのクラスがそれらすべてのインターフェースのメソッドを実装する必要があることを意味します。whciは、上記のような問題を引き起こしません。
あいまいさの問題があるため、Javaは多重継承、マルチパス、ハイブリッド継承をサポートしていません。
Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ? So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.
参照リンク: https : //plus.google.com/u/0/communities/102217496457095083679
誰もが知っている簡単な方法で、1つのクラスを継承(拡張)できますが、非常に多くのインターフェースを実装できます。Javaが非常に多くのクラスを拡張でき、それらが同じメソッドを持っていると仮定します。この時点で、サブクラスのスーパークラスメソッドを呼び出そうとすると、どのメソッドが実行されると思いますか?、コンパイラは混乱する例を取得し ます-複数の拡張を試みます が、これらのメソッドには、サブクラスで実装する必要のある本体がないインターフェースがあります。 複数の実装を試してみてください 。
* Javaの初心者なので、これは簡単な答えです*
3つのクラスX
がY
あり、Z
。
以下のように継承され、私たちはそうX extends Y, Z
両方Y
とZ
方法を持っているalphabet()
同じ戻り値の型と引数を持ちます。このメソッドalphabet()
でY
は、最初のアルファベットを表示し、メソッドアルファベットでZ
は、最後のアルファベットを表示します。したがって、alphabet()
がを呼び出すと、あいまいさが生じX
ます。それが最初または最後のアルファベットを表示するように言うかどうか??? したがって、Javaは多重継承をサポートしていません。インタフェースの場合には、考えるY
とZ
インターフェースします。したがって、どちらにもメソッドの宣言は含まれますalphabet()
が、定義は含まれません。最初のアルファベットと最後のアルファベットのどちらを表示するかはわかりませんが、メソッドを宣言するだけですalphabet()
。したがって、あいまいさを高める理由はありません。クラス内で必要なものを使用してメソッドを定義できますX
。
つまり、インターフェイスの定義は実装後に行われるため、混乱はありません。