私はこれを求めていません-> なぜJavaには多重継承がないのに、複数のインターフェースを実装することが許可されているのですか?
Javaでは、多重継承は許可されていませんが、Java 8の後に、インターフェースはdefaultメソッドを持つことができます(メソッド自体を実装できます) )、抽象クラスのように。このコンテキスト内では、多重継承も許可する必要があります。
interface TestInterface
{
// abstract method
public void square(int a);
// default method
default void show()
{
System.out.println("Default Method Executed");
}
}
物事はそれほど単純ではありません。
クラスが、同じシグネチャを持つデフォルトのメソッドを定義する複数のインターフェースを実装している場合、コンパイラは、クラスに対してこのメソッドをオーバーライドすることを強制します。
たとえば、次の2つのインターフェイスの場合:
public interface Foo {
default void doThat() {
// ...
}
}
public interface Bar {
default void doThat() {
// ...
}
}
コンパイルしません:
public class FooBar implements Foo, Bar{
}
メソッドを定義/オーバーライドして、あいまいさを解消する必要があります。
たとえば、次のようなBar
実装に委任できます。
public class FooBar implements Foo, Bar{
@Override
public void doThat() {
Bar.super.doThat();
}
}
または、次のようなFoo
実装に委任します。
public class FooBar implements Foo, Bar {
@Override
public void doThat() {
Foo.super.doThat();
}
}
または、まだ別の動作を定義します:
public class FooBar implements Foo, Bar {
@Override
public void doThat() {
// ...
}
}
この制約は、Javaがインターフェースのデフォルトメソッドであっても多重継承を許可しないことを示しています。
私は、主なものである複数の問題が発生する可能性があるため、複数の継承に同じロジックを適用できないと思います:
A
クラスとB
クラスで定義されたint foo
フィールドは同じではありません意味と意図。言語設計者はすでにそれについて考えていたので、これらのことはコンパイラーによって強制されます。だからあなたが定義する場合:
interface First {
default void go() {
}
}
interface Second {
default void go() {
}
}
そして、両方のインターフェースにクラスを実装します:
static class Impl implements First, Second {
}
コンパイルエラーが発生します。 go
をオーバーライドして、周囲にあいまいさを作成しないようにする必要があります。
しかし、次のようにすることで、ここでコンパイラをだますことができると考えるかもしれません。
interface First {
public default void go() {
}
}
static abstract class Second {
abstract void go();
}
static class Impl extends Second implements First {
}
First::go
はすでにSecond::go
の実装を提供していると考えることができます。これはあまりにも注意が必要なので、これもコンパイルされません。
JLS 9.4.1.3:同様に、署名が一致する抽象メソッドとデフォルトメソッドが継承されると、エラーが発生します。この場合、どちらかを優先することができます-おそらく、デフォルトメソッドも抽象メソッドの合理的な実装を提供すると仮定します。偶然の名前と署名以外に、デフォルトメソッドが抽象メソッドのコントラクトと一貫して動作することを信じる理由がないため、これは危険です-最初に開発されました。この状況では、デフォルトの実装が適切であることを積極的にアサートするようにユーザーに要求する方が安全です(オーバーライド宣言を介して)。
Javaに新たに追加された場合でも多重継承が許可されないことを固めるために、私が持ち込む最後のポイントは、インターフェースからの静的メソッドが継承されないことです。静的メソッドはデフォルトで継承されます:
static class Bug {
static void printIt() {
System.out.println("Bug...");
}
}
static class Spectre extends Bug {
static void test() {
printIt(); // this will work just fine
}
}
ただし、インターフェイスのそれを変更した場合(クラスとは異なり、複数のインターフェイスを実装できます):
interface Bug {
static void printIt() {
System.out.println("Bug...");
}
}
static class Spectre implements Bug {
static void test() {
printIt(); // this will not compile
}
}
現在、これはコンパイラおよびJLS
でも禁止されています。
JLS 8.4.8:クラスは、そのスーパーインターフェースから静的メソッドを継承しません。
Javaはフィールドの多重継承を許可しません。 JVMでのサポートは困難です。これは、ヘッダーがあるオブジェクトの先頭への参照のみを持つことができ、任意のメモリ位置ではないためです。
Oracle/Openjdkでは、オブジェクトにはヘッダーがあり、次にスーパークラスのフィールド、次にスーパークラスのフィールドなどが続きます。クラスのフィールドをヘッダーに対して異なるオフセットで表示できるようにすることは重要な変更です異なるサブクラスのオブジェクトの。ほとんどの場合、オブジェクト参照は、これをサポートするためにオブジェクトヘッダーへの参照およびフィールドへの参照になる必要があります。
それは主に「ダイヤモンドの問題」に関連しています。現在、同じメソッドで複数のインターフェースを実装する場合、コンパイラは、使用するメソッドがわからないため、実装するメソッドをオーバーライドするように強制します。 Java作成者は、インターフェイスがデフォルトのメソッドを使用できなかったときに、この問題を取り消したかったと思います。今、彼らはアイデアを思い付きました。それは、ストリーム/ラムダ式で機能的なインターフェイスとしてそれらを使用し、処理でデフォルトのメソッドを利用できるため、インターフェイスで実装されたメソッドを持つことができるのは良いことです。クラスでそれを行うことはできませんが、ダイヤモンドの問題はまだそこにあります。それは私の推測です:)
多重継承の主な問題は、順序付け(superのオーバーライドと呼び出し)、フィールド、およびコンストラクターです。インターフェイスにはフィールドやコンストラクタがないため、問題は発生しません。
他の言語を見ると、それらは通常2つの広いカテゴリに分類されます。
多重継承と特殊なケースを明確にするいくつかの機能を備えた言語:仮想継承[C++]、最も派生したクラスのすべてのスーパーコンストラクターへの直接呼び出し[C++]、スーパークラスの線形化[Python]、super[Python]など.
異なる概念を持つ言語、通常interfaces、traits、mixins、modulesなど。 Java]またはパラメーターを持つコンストラクター[ごく最近までのスカラ]、可変フィールド[Java]、オーバーライドの特定のルール(たとえば、ミックスインは基本クラス[Ruby]よりも優先されるため、多数のユーティリティメソッドが必要な場合に含めることができます)など。Javaはこれらのような言語になりました。
フィールドとコンストラクタを禁止するだけで、多重継承に関連する多くの問題を解決できるのはなぜですか?
インターフェースのdefault
メソッドには、次の問題があります。
実装されたインターフェースの両方が同じメソッドシグネチャでデフォルトメソッドを定義する場合、実装クラスは使用するデフォルトメソッドを認識しません。
実装クラスは、使用するデフォルトメソッドを明示的に指定するか、独自のメソッドを定義する必要があります。
したがって、Java-8のdefault
メソッドは多重継承を促進しません。デフォルトメソッドの背後にある主な動機は、ある時点で既存のインターフェースにメソッドを追加する必要がある場合、既存の実装クラスを変更せずにメソッドを追加できることです。このように、インターフェイスは古いバージョンと互換性があります。ただし、デフォルトメソッドを使用する動機を覚えておく必要があり、インターフェイスと実装の分離を維持する必要があります。