頭脳一式

人の記憶なんて曖昧なもの。すべての情報を頭に記憶するなんてナンセンス。困ったらここに来ればいいじゃん?というスタンスで最強のナレッジベースを目指すブログ

【Java】インターフェースの多重継承による菱形継承(ダイヤモンド継承)問題を理解する

ダイヤモンド継承でコンパイルエラーになる例

同じシグニチャを持つ複数のインターフェースをimplementsした場合、コンパイルエラーになる。

public interface ifParent {
    default void X(){
        System.out.println("interfaseParent");
    }
}
public interface ifA extends ifParent{
    @Override
    default void X(){
        System.out.println("interfaseA");
    }
}
public interface ifB extends ifParent{
    @Override
    default void X(){
        System.out.println("interfaseB");
    }
}
public class classC implements ifA ,ifB {   //ここでコンパイルエラーになる。

    public static void main(String[] args) {
        classC c = new classC();
        c.X();
    }
}

上記のソースをクラス図で表現すると以下のとおり。
f:id:shiakisudev:20181104191414j:plain classCがifAとifBをimplementsしたタイミングでコンパイルエラーになる。
これはclassCから見た場合、ifAとifBが同階層にあり、両者ともdefaultメソッドをOverrideしているため。
これを防ぐには以下のようにclassCでXメソッドをOverrideする必要がある。

public class classC implements ifA ,ifB {

    public static void main(String[] args) {
        classC c = new classC();
        c.X();  //classC
    }

    @Override
    public void X(){
        System.out.println("classC");
    }
}

ダイヤモンド継承でコンパイルエラーにならない例

複数のインターフェースが同階層にあってもメソッドが異なる階層(距離が離れている場合)近いほうのメソッドが呼ばれるため、コンパイルエラーにならない。

public interface ifParent {
    default void X(){
        System.out.println("interfaseParent");
    }
}
public interface ifA extends ifParent{
    @Override
    default void X(){
        System.out.println("interfaseA");
    }
}
public interface ifB extends ifParent{
}
public class classC implements ifA ,ifB {  

    public static void main(String[] args) {
        classC c = new classC();
        c.X();  //interfaseA
    }
}

上記ソースをクラス図で表現すると以下のとおり。
f:id:shiakisudev:20181104191431j:plain ダイヤモンド継承でコンパイルエラーになる例との違いはifBでOverrideしていない点。
より近い方でOverrideしたifAのXメソッドが呼ばれる。