読者です 読者をやめる 読者になる 読者になる

Javaのインターフェースでデフォルトメソッドを実装する

javaのインターフェースでデフォルトメソッドを実装したメモです。

デフォルトメソッド

java8からインターフェースでデフォルトメソッドが記述できるようになりました。

デフォルトメソッド実装

デフォルトメソッドの記述方法は、メソッドにdefaultキーワードをつけます。
メソッド自体は普通のメソッドと同様に記述します。

    interface PrintWord {
        default String print(String word) {
            return "[" + word + "]";
        }
    }
デフォルトメソッド使用

デフォルトメソッドを使用する場合は、インターフェースの実装クラスを作成して使用します。

    PrintWord p = new PrintWord() {};
    String actual = p.print("abc");

テストコード。

    @Test
    public void デフォルトメソッド使用() throws Exception {
        PrintWord p = new PrintWord() {};
        String actual = p.print("abc");
        String expected = "[abc]";
        assertThat(actual).isEqualTo(expected);
    }
デフォルトメソッドのオーバーライド

デフォルトメソッドを定義したインターフェースを継承して、デフォルトメソッドでオーバーライドできます。

    interface MorePrintWord extends PrintWord {
        @Override
        default String print(String word) {
            return "[[[[[" + word + "]]]]]";
        }
    }

使用する場合は、同様にインターフェースの実装クラスを作成して使用します。

        PrintWord p = new MorePrintWord() {};
        String actual = p.print("abc");

テストコード。

    @Test
    public void デフォルトメソッドのオーバーライドの確認() throws Exception {
        PrintWord p = new MorePrintWord() {};
        String actual = p.print("abc");
        String expected = "[[[[[abc]]]]]";
        assertThat(actual).isEqualTo(expected);
    }
デフォルトメソッドの抽象化

デフォルトメソッドを定義したインターフェースを継承して、抽象メソッドとしてオーバーライドできます。

    interface AbstractPrintWord extends PrintWord {
        @Override
        String print(String word);
    }

使用する場合は、インターフェースを実装したクラスでメソッドを実装して使用。

        PrintWord p = new AbstractPrintWord() {
            @Override
            public String print(String word) {
                return "[[[" + word + "]]]";
            }
        };

        String actual = p.print("abc");

テストコード。

    @Test
    public void 再抽象化のメソッド実装() throws Exception {
        PrintWord p = new AbstractPrintWord() {
            @Override
            public String print(String word) {
                return "[[[" + word + "]]]";
            }
        };

        String actual = p.print("abc");
        String expected = "[[[abc]]]";
        assertThat(actual).isEqualTo(expected);
    }
多重継承

インターフェースは複数実装できますが、同じデフォルトメソッドを持ったインターフェースを実装する場合、
実装クラスでオーバーライドする必要があります。

Rootというインターフェースがあり、それを継承したimplAとimplBというインターフェースを用意します。
implAとimplBで同じ名前のデフォルトメソッド method()を定義します。

    interface Root {
        String method(String str);
    }

    interface implA extends Root {
        @Override
        default String method(String str) {
            return "A " + str;
        }
    }

    interface implB extends Root {
        @Override
        default String method(String str) {
            return "B " + str;
        }
    }

implAとimplBを実装したImplAandBというクラスを実装します。
この場合、implAとimplBに同じメソッドがあるため、どちらを使っていいか分からないため、
コンパイルエラーになります。

    // コンパイルエラー
    class ImplAandB implements implA, implB {
        
    }

コンパイルエラーを解消するために、インターフェースで定義されているデフォルトメソッドを
オーバーライドして実装します。
インターフェースに同じ名前のメソッドがある場合は必ずオーバーライドする必要があります。

    class ImplAandB implements implA, implB {
        @Override
        public String method(String str) {
            return "A and B " + str;
        }
    }

テストコード。

    @Test
    public void インターフェースの多重継承() throws Exception {
        ImplAandB ab = new ImplAandB();
        String actual = ab.method("ab");
        String expected = "A and B ab";

        assertThat(actual).isEqualTo(expected);
    }

終わり。

ソースは一応あげときました。

github.com