今回やること
インターフェースは、クラスが提供する操作の契約です。内容が違うReportとLabelへ「印刷できる」という共通のPrintable契約を付けます。
前提: Java入門 #21 - 継承と#22 - ポリモーフィズムを終えていること。
Printable契約: printBody()を持つ
├── Report implements Printable
└── Label implements Printable
1. 契約を宣言する
interface Printable {
void printBody();
}
本体のないprintBody()は、実装クラスが必ず用意する操作です。インターフェース内の抽象メソッドは暗黙にpublic abstractですが、入門では短い形を使います。
インターフェース自体はnew Printable()で作れません。契約を実装したクラスのオブジェクトを作ります。
2. implementsで実装する
class Report implements Printable {
@Override
public void printBody() {
System.out.println("月次レポート");
}
}
implements Printableと書いた具体クラスは、printBody()を実装します。インターフェースのメソッドはpublicなので、実装側もpublicが必要です。
@Overrideを付けると、名前や引数を間違えた時にコンパイラが検出します。
3. defaultメソッドを一つ追加する
defaultを付けると、インターフェースに共通の実装を置けます。
default void printHeader() {
System.out.println("--- 印刷開始 ---");
}
実装クラスは、そのまま共通処理を利用できます。すべてをdefaultにせず、共通して同じ動きになる小さな処理に使います。
完成コードを動かす
InterfaceDemo.javaとして保存します。
interface Printable {
void printBody();
default void printHeader() {
System.out.println("--- 印刷開始 ---");
}
}
class Report implements Printable {
@Override
public void printBody() {
System.out.println("月次レポート");
}
}
class Label implements Printable {
@Override
public void printBody() {
System.out.println("配送ラベル");
}
}
public class InterfaceDemo {
public static void main(String[] args) {
Printable[] items = {new Report(), new Label()};
for (Printable item : items) {
item.printHeader();
item.printBody();
}
}
}
javac InterfaceDemo.java
java InterfaceDemo
配列の型をPrintableにすると、元のクラスが異なる2つを同じ「印刷できるもの」として扱えます。呼び出せる操作はPrintableの契約にあるものです。
抽象クラスとの短い違い
| 欲しいもの | 選択の目安 |
|---|---|
| 異なる種類へ同じ操作を約束したい | インターフェース |
| 密接な親子で状態やコンストラクタを共有したい | 抽象クラス |
Javaのクラスは複数のインターフェースを実装できます。一方、クラスの直接継承は1つです。抽象クラスの具体例は次の記事で扱います。
成功確認
--- 印刷開始 ---
月次レポート
--- 印刷開始 ---
配送ラベル
2クラスが同じPrintable型として扱われ、共通headerと個別bodyを表示できれば成功です。
よくあるつまずき
実装メソッドへpublicを付けない
インターフェースのpublicメソッドを、より狭いアクセス範囲にはできません。実装側をpublicにします。
interfaceを直接newする
契約だけでは実体を作れません。new Report()のように実装クラスを作り、Printable型の変数へ代入します。
共有状態までinterfaceへ持たせる
インターフェースのフィールドは定数です。オブジェクトごとの状態やコンストラクタを共有したいなら、次の抽象クラスを検討します。
練習
Playableインターフェースにplay()とdefaultのshowReady()を定義し、MusicとVideoで実装してください。Playable[]から両方を呼びます。
次のステップ
Java入門 #24 - 抽象クラスで、共通状態と未実装メソッドを同じ親クラスへ置く方法を学びます。