2014年12月22日月曜日

[Java8]インタフェースのデフォルトメソッド

Java8から新しく追加されたインタフェースのデフォルトメソッド。

デフォルトメソッドの定義

デフォルトメソッドは、defaultキーワードと共にメソッドを宣言します。
この例では、printメソッドがデフォルトメソッドとして宣言されています。
interface Hoge {
    default void print() {
        System.out.println("hoge");
    }
}

同じデフォルトメソッドを持つ複数のインタフェースを実装した場合

何もしなければコンパイルエラーとなります。
例えば、下のコードではImplクラスが同じ定義のデフォルトメソッドを持つI1とI2インターフェースを実装しているので、コンパイル時にエラーとして報告されます。
interface I1 {
    default void print() {
        System.out.println("I1");
    }
}
interface I2 {
    default void print() {
        System.out.println("I2");
    }
}

class Impl implements I1, I2 {
}

もしどちらか一方の実装を使用したいのであれば、superキーワードを使用して一方のインタフェースの実装を選択します。
下の例では、I2インタフェースのデフォルト実装を選択しています。
class Impl implements I1, I2 {
    @Override
    public void print() {
        I2.super.print();
    }
}

もちろん、下のコードのように実装を上書きすることもできます。
class Impl implements I1, I2 {
    @Override
    public void print() {
        System.out.println("Impl");
    }
}

片方がデフォルトメソッドで片方が抽象メソッドの場合も、コンパイルエラーとなります。
この場合も、上で説明したようにデフォルトメソッドを選択するか、このクラスで新たな実装をするか選択する必要があります。
interface I1 {
    default void print() {
        System.out.println("I1");
    }
}
interface I2 {
    void print();
}

class Impl implements I1, I2 {
}

一方が抽象クラスの場合には、抽象クラスに定義されたメソッドが優先されて選択されます。
例えば下のコードの場合、A1抽象クラスに定義されたprintメソッドが呼び出されます。
interface I1 {
    default void print() {
        System.out.println("I1");
    }
}

abstract class A1 {
    public void print() {
        System.out.println("A1");
    }
}

class Impl extends A1 implements I1 {
}