【JavaGold 実践】defaultメソッドの多重継承

JavaSE8 Goldの黒本・紫本の一周目が完了しました。
…が、覚えること多すぎてサッパリダメだ…!

サーっと読んで問題解くだけじゃ身に付きませんね。。
2周目以降、躓いたところは実際にコードを書いて復習したいと思います。

今回は、インターフェースのdefaultメソッドの仕様について。
同一のシグニチャを持つdefaultメソッドを多重継承してみます。

1. 継承関係のないインターフェースの場合

interface Parent1 {
    default void print() {
        System.out.println("Parent1");
    }
}

interface Parent2 {
    default void print() {
        System.out.println("Parent2");
    }
}

class Child implements Parent1, Parent2 {   // コンパイルエラー発生
 // オーバーライドなし
    public static void main(String args[]){
        Child c = new Child();
        c.print();
    }
}

エラーの内容:Child inherits unrelated defaults for print() from types Parent1 and Parent2.

デフォルトメソッド同士に関係性がないと、継承しちゃダメですよーって意味でしょうか。Childのprintメソッドを実行したとき、どちらのインターフェースの処理を実行すればよいかが分からないのがアカンと。

2. 実装したクラスでオーバーライド

interface Parent1 {
    default void print() {
        System.out.println("Parent1");
    }
}

interface Parent2 {
    default void print() {
        System.out.println("Parent2");
    }
}

class Child implements Parent1, Parent2 {
    @Override
    public void print() {
        System.out.println("Child");
    }

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

コンパイルエラーが消えました。Childのprintメソッドで実行する内容が確定したからですね。
実行すると「Child」が表示されました。

3. インターフェースに継承関係を持たせる

interface Parent1 {
    default void print() {
        System.out.println("Parent1");
    }
}

interface Parent2 extends Parent1 {
    default void print() {
        System.out.println("Parent2");
    }
}

class Child implements Parent1, Parent2 {
 // オーバーライドなし
    public static void main(String args[]){
        Child c = new Child();
        c.print();
    }
}

この場合はコンパイルエラーが発生しません。
実行すると「Parent2」が表示されました。
継承関係で一番下のインターフェースで定義されたメソッドが一番新しい定義=最優先で実装される、っていう感じなのかなあ。

4. 子インターフェースを多重継承

interface Parent {
    default void print() {
        System.out.println("Parent1");
    }
}

interface Child1 extends Parent {
    default void print() {
        System.out.println("Child1");
    }
}

interface Child2 extends Parent {
    default void print() {
        System.out.println("Child1");
    }
}

class GrandChild implements Child1, Child2 {  // コンパイルエラー発生

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

Child1とChild2はそれぞれParentを継承。
しかし、Child1とChild2自体には継承関係がないため、このパターンでもコンパイルエラーが発生します。

まとめ

  • 継承関係のないデフォルトメソッドを多重継承するとコンパイルエラー
  • 実装クラス・インターフェースでデフォルトメソッドをオーバーライドすればOK
  • 継承関係のあるインターフェースだったらデフォルトメソッドを多重継承しても問題なし。継承されるのは最下層の子インターフェースのメソッド