多重継承を実現するにはインターフェイスを使用する必要がありますが、インターフェイス メソッドに本体がなく、派生クラスでオーバーライドする必要があるのはなぜでしょうか。
私は本当に明快な答えが欲しいのですが、コンピューターの専門用語があまり含まれていません。これを理解できないようです。さまざまな参考文献を参照しました
多重継承を実現するにはインターフェイスを使用する必要がありますが、インターフェイス メソッドに本体がなく、派生クラスでオーバーライドする必要があるのはなぜでしょうか。
私は本当に明快な答えが欲しいのですが、コンピューターの専門用語があまり含まれていません。これを理解できないようです。さまざまな参考文献を参照しました
Java は、C++ や Eiffel などの言語とは対照的に、型の多重継承(つまり、インターフェースと 1 つのクラス)しか持たず、状態と動作の多重継承がないためです。後者は非常に複雑になります (特に状態)。
Java 設計者 (および C#) は、C++ プログラマーにデバッグが非常に困難な問題を提示することが多かったため、これを含めないことを選択しました。複数のインターフェイスを実装することで、真の多重継承を必要とするほとんどの問題を解決できるため、トレードオフは価値があると見なされました。
動作(状態ではない)の複数の継承は、インターフェイスが別のクラスのメソッドに委譲するメソッドを宣言できる仮想拡張メソッドの形式で Java 8 に導入される可能性があることに注意してください。これは、そのインターフェイスを実装するすべての型に存在します。
インターフェイスは、実装クラスが提供するサービスを宣言します。HOW ではありません (これは実装クラスの仕事です)。多重継承は、複雑なコードとクラス階層につながるため、良くないと見なされています。
インターフェイスには、定数変数 (public + static + final) と抽象メソッド (public & abstract) しかありません。これらは、インターフェースを実装するクラスによって使用されることを意図しています。
インターフェイスは単に「契約です」と言うだけで、使用したい場合は、いくつかの規則に従う必要があります(すべての抽象メソッドに実装を与えます)。
ダイアモンドの問題を回避するために、クラスが 1 つのクラスのみを拡張できるようにすることで、Java では多重継承が省略されます。とにかく、インターフェイスを使用して、Java で型の複数の継承を行うことができます。
簡単な答え:
インターフェイスは、実装の標準を提供します。
説明: Java では、メンバーが実装されていない
という点で、インターフェースは抽象クラスに似ています。例えば、
public interface Comparable
{ boolean less(Object m);
boolean greater(Object m);
boolean lessEqual(Object m);
boolean greaterEqual(Object m);
}
インターフェイスは、実装の標準を提供します。
インターフェイスを使用する利点は、多重継承をシミュレートできることです。Java のすべてのクラスには、1 つの基本クラスが必要です。唯一の例外はjava.lang.Object
(Java 型システムのルート クラス) です。Java では、クラスの多重継承は許可されていません。
すべてのインスタンス メソッドは暗黙的public
に andabstract
です。そのようにマークすることはできますが、マークは時代遅れの慣行と見なされるため、そうしないことをお勧めします。インターフェイス自体はパブリックである必要はなく、標準ライブラリのいくつかのインターフェイスはパブリックではないため、内部でのみ使用されます。
インターフェイスは、クラスが実装できるプロトコルを作成します。クラスを拡張できるのと同じように、(新しいインターフェースを取得するために) インターフェースを拡張できることに注意してください。実際には、複数のインターフェースを拡張できます。したがって、インターフェイスは多重継承の利点を享受します。(クラスはそうではありません。)インターフェイスの多重継承にはほとんど欠点がありません (小さな名前の競合の問題は 1 つの例外です)。のような実装の多重継承には大きな欠点があります。これらには効率が含まれますC++
いくつかの状況でどのコードが実行されるかを決定することの意味上の困難と同様に、考慮事項です。
Comparableを実装するPolynomialクラスは、インターフェイスで宣言されたすべての関数を実装する必要があります。
public class Polynomial implements Comparable
{ . . .
boolean less(Object m){ . . . }
boolean greater(Object m){ . . . }
boolean lessEqual(Object m){ . . . }
boolean greaterEqual(Object m){ . . . }
Polynomial multiply(Polynomial P){ . . . }
. . .
}
クラスは、任意の数のインターフェースを実装することを選択できます。インターフェイスを実装するクラスは、そのインターフェイスのすべてのメソッドに本体を提供する必要があります。また、抽象クラスはインターフェースの一部を実装することを選択し、残りを非抽象サブクラスに残すことができると期待しています。
インターフェイスの有用性は、単に他のプログラマーのためにプロトコルを公開するだけではありません。すべての関数は、インターフェイス型のパラメーターを持つことができます。インターフェイスを実装するクラスの任意のオブジェクトを引数として渡すことができます。
Java インターフェースには、インターフェースを実装するクラスによって実装する必要があるメソッドのリストが含まれています。したがって、メソッドには本体がありません。各メソッドの本体は実装クラスにあります。
インターフェイスメソッドには本体がありません
public interface Flyable
{
public void fly();
}
インターフェイス自体は何もしないため
インターフェイスはコントラクトを定義します。
一方、インターフェースは、クラスが何であるかではなく、クラスができることを定義します。したがって、インターフェースは通常動詞に関するものです。
したがって、Flyable インターフェースは、埋め込まれた FlyableObject が飛行するというコントラクトを定義するだけです。
お気に入り:
class Rocket implements Flyable
{
public void fly()
{
// do the stuffs.
}
}
もちろん、多重継承もインターフェイスを介してのみ実現できます。
「なぜJavaは実装の多重継承をサポートしないのですか?」と尋ねています。
これは Java チュートリアルのMultiple Inheritance of State, Implementation, and Typeで説明されていますが、実装の多重継承の問題 (および最後に新しい言語機能の解決策) の具体的な例を示したいと思います。
同じ名前のメソッドを定義する 2 つのインターフェース (インターフェース・メソッド本体を許可する Java の提案バージョン) を想像してみてください。
public interface FaceOne {
public void method() {
System.out.println("FaceOne Version");
}
}
public interface FaceTwo {
public void method() {
System.out.println("FaceTwo Version");
}
}
クラスは両方のインターフェースを実装しますが、メソッドをオーバーライドしません。
public class Inheriter implements FaceOne, FaceTwo {
}
クラスが先祖からメソッドを継承するため機能する を呼び出すとInheriter.method()
、問題が発生します。出力は「FaceOne Version」または「FaceTwo Version」を出力しますか?
さらに、クラスがメソッドをオーバーライドするが、 を使用して祖先のバージョンも呼び出したいsuper
場合、コンパイラはメソッドのバージョンを選択するのに再び問題が発生します。
これが、Java が実装の多重継承をサポートしていない理由です。
余談ですが、これを言語に実装するエレガントな方法は次のようになると思います。
クラスの実装に祖先インターフェースのメソッドのオーバーライドを強制し続けます。これにより、オーバーライドされていないメソッドの最初の問題が解決されます。
次に、内部クラスの外側のインスタンスにアクセスする場合と同様の表記法を使用して、 を使用して特定の祖先インターフェイスにアクセスしますsuper
。Inheriter クラスには複数のオプションがあります。
を呼び出さずsuper.method()
に、新しく定義された実装のみを使用してください。
FaceOne.super.method()
デフォルトの継承された実装出力を「FaceOne バージョン」にするために使用します。
FaceTwo.super.method()
デフォルトの継承された実装出力を「FaceTwo バージョン」にするために使用します。
上記の組み合わせを使用します。
1 つの実装は次のようになります。
@Override
public void method() {
FaceOne.super.method();
FaceTwo.super.method();
System.out.println("Inheriter Version");
}
出力:
FaceOneバージョン
FaceTwo バージョン
継承バージョン
編集: this questionによると、これは明らかに Java 8 でデフォルトの実装がどのように構成されているかです。