3秒でわかる
実装を持たずメソッドの並びだけを決める型。呼ぶ側を具体的なクラスから切り離し、後から差し替えられる設計にするために使います。
30秒図解
もう少し詳しく
どういうものか
interface は「このメソッドを持っていること」だけを宣言する型です。中身の処理は書かず、実装するクラス側が用意します。Java では implements で宣言し、一つのクラスがいくつでも実装できます。抽象クラスと違ってフィールドの状態を持たないため、継承の縦の関係とは別に、横断的な役割を後付けできます。
Java 8 以降は default メソッドで既定の実装を持たせられるようになり、static メソッドも書けます。それでもインスタンスの状態を持てない点は変わりません。
なぜ必要か
呼ぶ側が具体的なクラス名を知っていると、差し替えのたびに呼ぶ側まで書き換えることになります。引数の型を interface にしておけば、通知をメールからチャットへ変えても、送信処理を受け取る側のコードは一行も変わりません。テストのときに本物の外部サービスの代わりに記録だけする実装を渡せるのも同じ理由です。
具体例
public interface Notifier {
void send(String message);
}
public class MailNotifier implements Notifier {
@Override
public void send(String message) {
System.out.println("mail " + message);
}
}
public class OrderService {
private final Notifier notifier;
public OrderService(Notifier notifier) {
this.notifier = notifier;
}
public void complete(String orderId) {
notifier.send("order done " + orderId);
}
}OrderService は MailNotifier を知りません。コンストラクタに何を渡すかだけで振る舞いが決まります。
つまずきやすいところ
まず、interface のメソッドは既定で public です。実装側でうっかり修飾子を省くと、可視性が下げられないというエラーになります。
次に、実装忘れです。抽象メソッドを一つでも書き残すとコンパイルが通らないので、実装のつもりがオーバーロードになっていないか(引数の型が違っていないか)を確認します。@Override を付けておけば、この取り違えはコンパイル時に見つかります。
もう一つ、interface を作ること自体が目的になりがちです。実装が一つしかなく差し替える予定もないなら、素直にクラスのままにしておくほうが読みやすくなります。
似た用語との違い
| 比較 | interface | 抽象クラス |
|---|---|---|
| 状態 | 持てない | フィールドを持てる |
| 多重 | 複数実装できる | 継承は一つだけ |
| 使い分け | 役割や契約を表す | 共通の実装を共有する |
覚え方
interface は「何ができるか」の一覧、クラスは「どうやるか」の中身。呼ぶ側には一覧だけを見せる、と考えると設計が整理できます。