Java 8では、デフォルトメソッドという概念が登場しました。これは、インターフェース内で実装が定義されたメソッドです。これにより、既存の実装との互換性を損なうことなく、インターフェースに新しいメソッドを追加することができます。
構文:
public interface MyInterface { default void printHello() { System.out.println("Hello!"); } }
特徴と注意点:
Objectクラスのメソッド(例えば、equals、hashCode)をオーバーライドすることはできません。もしもクラスが同じデフォルトメソッドを持つ2つのインターフェースを実装し、これをオーバーライドしなかった場合、どうなるか?
答え: コンパイラは「class... inherits unrelated defaults for ... from types ... and ...」というエラーを出し、クラス内でこのメソッドを明示的に実装する必要があります。
例:
interface A { default void doSomething() { System.out.println("A"); } } interface B { default void doSomething() { System.out.println("B"); } } class C implements A, B {} // コンパイルエラー!
解決策:
class C implements A, B { @Override public void doSomething() { A.super.doSomething(); // または B.super.doSomething() } }
物語
チームプロジェクトでAPIの移行のために、共通インターフェースにデフォルトメソッドを追加しました。古いインターフェースの実装は新しいメソッドをオーバーライドせず、デフォルトの実装が期待されたロジックの代わりに実行されたため、予期しない動作が発生しました。その結果、ユーザーは機能の低下に気付きました。
物語
ライブラリを拡張する際に、開発者の一人が共通インターフェースにビジネスロジックを持つデフォルトメソッドを追加しました。その後、別のインターフェースに異なる動作を持つ同様のメソッドを追加したことで、継承の競合が発生し、新しい実装をコンパイルできなくなりました。
物語
開発者は、インターフェース内で
hashCodeという名前のデフォルトメソッドを使用しようとし、このことがサブクラスでのメソッドのオーバーライドに影響を与えることを期待していましたが、コンパイラはそれを許可しませんでした。これはエラーの原因を長時間調査することになり、インターフェースの構造の再設計につながりました。