同じメソッド名とシグネチャを持つ2つのインターフェイス。しかし、単一のクラスによって実装されている場合、コンパイラはどのメソッドがどのインターフェイス用であるかをどのように識別するのでしょうか?
例:
interface A{
int f();
}
interface B{
int f();
}
class Test implements A, B{
public static void main(String... args) throws Exception{
}
@Override
public int f() { // from which interface A or B
return 0;
}
}
型が2つのインターフェイスを実装し、それぞれのinterface
が同じシグネチャを持つメソッドを定義する場合、実際には1つのメソッドのみが存在し、それらは区別できません。たとえば、2つのメソッドの戻り値の型が競合している場合、コンパイルエラーになります。これは、継承、メソッドのオーバーライド、非表示、および宣言の一般的なルールであり、継承された2つのinterface
メソッド間だけでなく、interface
メソッドとsuper class
メソッド間、またはジェネリックの型消去による競合さえも起こり得る競合にも適用されます。
以下に、interface Gift
にpresent()
メソッド(ギフトの提示など)があり、interface Guest
にもpresent()
メソッド(例:ゲストが存在し、不在ではない)がある例を示します。
Presentable johnny
は、Gift
とGuest
の両方です。
public class InterfaceTest {
interface Gift { void present(); }
interface Guest { void present(); }
interface Presentable extends Gift, Guest { }
public static void main(String[] args) {
Presentable johnny = new Presentable() {
@Override public void present() {
System.out.println("Heeeereee's Johnny!!!");
}
};
johnny.present(); // "Heeeereee's Johnny!!!"
((Gift) johnny).present(); // "Heeeereee's Johnny!!!"
((Guest) johnny).present(); // "Heeeereee's Johnny!!!"
Gift johnnyAsGift = (Gift) johnny;
johnnyAsGift.present(); // "Heeeereee's Johnny!!!"
Guest johnnyAsGuest = (Guest) johnny;
johnnyAsGuest.present(); // "Heeeereee's Johnny!!!"
}
}
上記のスニペットはコンパイルおよび実行されます。
は1つだけであることに注意してください@Override
必要!!!これは、Gift.present()
とGuest.present()
が "@Override
- equivalent"( JLS 8.4.2 )であるためです。
したがって、johnny
にはpresent()
の実装が1つだけあります。また、johnny
としてもGift
としても、Guest
の処理方法は関係ありません。呼び出すメソッドは1つだけです。
次に、継承された2つのメソッドが@Override
- equivalentではない例を示します。
public class InterfaceTest {
interface Gift { void present(); }
interface Guest { boolean present(); }
interface Presentable extends Gift, Guest { } // DOES NOT COMPILE!!!
// "types InterfaceTest.Guest and InterfaceTest.Gift are incompatible;
// both define present(), but with unrelated return types"
}
これは、interface
からメンバーを継承することは、メンバー宣言の一般的な規則に従う必要があることをさらに繰り返します。ここにGift
とGuest
があり、戻り値の型が互換性のないpresent()
を定義しています。1つはvoid
、もう1つはboolean
です。 1つの型でvoid present()
とboolean present()
を使用できないのと同じ理由で、この例ではコンパイルエラーが発生します。
メソッドのオーバーライドと非表示の通常の要件に従って、@Override
-同等のメソッドを継承できます。それらはが@Override
-- equivalentであるため、実装するメソッドは事実上1つだけであり、したがって区別/選択するものは何もありません。
コンパイラは、@Override
- equivalentであると判断されると同じメソッドであるため、どのメソッドがどのインターフェイス用であるかを識別する必要はありません。
潜在的な非互換性を解決するのは難しい作業かもしれませんが、それはまったく別の問題です。
コンパイラに関する限り、これらの2つの方法は同じです。両方の実装が1つあります。
2つのメソッドが同じ実装である必要があるという点で、2つのメソッドが実質的に同一である場合、これは問題ではありません。それらが契約上異なる場合(各インターフェイスのドキュメントに従って)、問題が発生します。
これはこの質問の重複としてマークされました https://stackoverflow.com/questions/24401064/understanding-and-solving-the-diamond-problems-in-Java
多重継承の問題を取得するにはJava 8が必要ですが、それでもまだダイアモン問題ではありません。
interface A {
default void hi() { System.out.println("A"); }
}
interface B {
default void hi() { System.out.println("B"); }
}
class AB implements A, B { // won't compile
}
new AB().hi(); // won't compile.
JB Nizetがコメントしているように、これは私のオーバーライドを修正できます。
class AB implements A, B {
public void hi() { A.super.hi(); }
}
ただし、問題はありません
interface D extends A { }
interface E extends A { }
interface F extends A {
default void hi() { System.out.println("F"); }
}
class DE implement D, E { }
new DE().hi(); // prints A
class DEF implement D, E, F { }
new DEF().hi(); // prints F as it is closer in the heirarchy than A.
特定するものは何もありません。インターフェイスは、メソッド名と署名のみを禁止します。両方のインターフェイスにまったく同じ名前とシグネチャのメソッドがある場合、実装クラスは単一の具象メソッドで両方のインターフェイスメソッドを実装できます。
ただし、2つのインターフェイスメソッドのsemanticコントラクトが矛盾する場合は、ほとんど失われています。その場合、単一のクラスに両方のインターフェースを実装することはできません。
インターフェイスと同様に、メソッドを宣言しているだけです。これら両方のインターフェイスを実装する具体的なクラスは、メソッドが1つしかないことを理解しています(両方のメソッドで、戻り値の型が同じであると説明しました)。したがって、問題はないはずです。具体的なクラスでそのメソッドを定義できます。
ただし、2つのインターフェイスに同じ名前で異なる戻り型のメソッドがあり、具象クラスに2つのメソッドを実装する場合:
以下のコードを見てください:
public interface InterfaceA {
public void print();
}
public interface InterfaceB {
public int print();
}
public class ClassAB implements InterfaceA, InterfaceB {
public void print()
{
System.out.println("Inside InterfaceA");
}
public int print()
{
System.out.println("Inside InterfaceB");
return 5;
}
}
コンパイラがメソッド「public void print()」を取得すると、最初にInterfaceAを検索して取得しますが、戻り値の型がInterfaceBのメソッドと互換性がないというコンパイル時エラーが発生します。
そのため、コンパイラにとっては困ります。
この方法では、同じ名前のメソッドで戻り型が異なる2つのインターフェイスを実装することはできません。
インターフェースを匿名として実装してみてください。
public class MyClass extends MySuperClass implements MyInterface{
MyInterface myInterface = new MyInterface(){
/* Overrided method from interface */
@override
public void method1(){
}
};
/* Overrided method from superclass*/
@override
public void method1(){
}
}
両方が同じであれば、それは問題ではありません。インターフェイスメソッドごとに単一の具象メソッドを使用して、これらの両方を実装します。