1. インターフェースとは?
抽象クラスが家の設計図だとすれば、インターフェースはむしろ特定の作業を行うという「契約」に近いものです。たとえば、水道工事を依頼するとき、あなたは「蛇口を直す」「漏れを止める」といったことができると期待します。どうやって実現するかは気にしません。結果が出れば十分です。Java におけるインターフェースはまさにその契約であり、どのメソッドを実装すべきかは定めるものの、どのように実装するかは規定しません。
要点だけ簡潔に:
インターフェースは、Java の特別な型で、そのインターフェースに「サイン」したクラスが実装しなければならないメソッド群を定義します。
- インターフェースは「何をするか」だけを記述し、「どうやってするか」は記述しません。
- Java 8 までは、インターフェースにメソッドの実装は含まれませんでした。
- インターフェースは純粋な契約です。クラスがインターフェースを実装するなら、そのすべてのメソッドを実装する必要があります。
なぜ便利なのか?
- 1 つのクラスに複数のインターフェースを「付与」でき、複数の「役割」を与えられます。
- クラスがさまざまな機能を実装できるため、柔軟で拡張可能なアーキテクチャを構築できます。
- インターフェースは Java の標準ライブラリで広く使われています(例: Comparable、Serializable、Runnable など)。
2. インターフェースの宣言構文
Java でインターフェースを宣言するのはとても簡単です。キーワード interface を使います。Java 8 までは、インターフェースのメソッドは明示しなくてもデフォルトで public abstract と見なされました。つまり、そのインターフェースを実装するクラスでメソッドを実装しなければならない、ということです。
インターフェースの例
public interface Movable {
void move(int x, int y);
}
- ここではインターフェース Movable を宣言しています(「動かせる」「動く能力をもつ」といった意味に訳せます)。
- 内部にはメソッド move(int x, int y) が宣言されています。メソッド本体はなく、シグネチャのみです。これが「契約」です。すなわち「Movable を実装するなら、move を実装しなさい」ということです。
構文上の特徴
- インターフェースのメソッドは(Java 8 までは)本体を持ちません。
- インターフェースのすべてのメソッドはデフォルトで public abstract(明示不要)です。
- インターフェースには定数(public static final)のみを含められ、通常のフィールドは持てません。
定数の例
public interface Constants {
int MAX_SPEED = 100; // 既定で public static final
}
3. インターフェースとクラスの違い
インターフェースはクラスではありません! その違いを整理しましょう。
| クラス(抽象クラスを含む) | インターフェース |
|---|---|
| フィールド(状態)を持てる | フィールドは持てない(定数のみ) |
| メソッドの実装を含められる | Java 8 までは実装を含められず、シグネチャのみ |
| new で生成できる(抽象でなければ) | 直接は生成できない |
| 継承は 1 つだけ(extends) | クラスは複数のインターフェースを実装できる(implements) |
| 「それは何か」を表す | 「何ができるか」を表す |
クラスは次のようにしてインターフェースを実装します: implements
public class Robot implements Movable {
@Override
public void move(int x, int y) {
System.out.println("ロボットは座標 (" + x + ", " + y + ") に移動します");
}
}
- Robot クラスは Movable インターフェースを実装しています。
- キーワード implements は「実装する」の意味です。
- インターフェースのすべてのメソッドを実装する必要があります(実装しないとコンパイルエラー)。
- アノテーション @Override を忘れずに。必須ではありませんが、コンパイラやあなた自身のミス防止に役立ちます。
4. インターフェースの使用例
シンプルな例を書いて、実際の動きを見てみましょう。インターフェース Movable があり、ロボット、車、動物など、さまざまなクラスが「動ける」ようにしたいとします。
ステップ 1. インターフェースを宣言する
public interface Movable {
void move(int x, int y);
}
ステップ 2. クラスでインターフェースを実装する
public class Robot implements Movable {
@Override
public void move(int x, int y) {
System.out.println("ロボットは座標 (" + x + ", " + y + ") に移動します");
}
}
ステップ 3. インターフェースを使う
public class Main {
public static void main(String[] args) {
Movable m = new Robot(); // インターフェース型の変数!
m.move(10, 20); // 出力: ロボットは座標 (10, 20) に移動します
}
}
注意
- インターフェース型の変数(Movable m)を宣言し、そのインターフェースを実装したクラスのオブジェクト(new Robot())を代入できます。
- これにより、実際のクラスが何であるかを知らなくても、あらゆる「動ける」オブジェクトで動作する汎用的なコードが書けます。
メソッドを実装しない場合のコンパイルエラー
Error: Class 'Robot' must either be declared abstract or implement abstract method 'move(int, int)' in 'Movable'
これこそがインターフェースの力です。インターフェースは、すべての実装クラスが必要なメソッドを保証するのです。
5. Java 標準ライブラリのインターフェース
インターフェースは単なる「学校の話題」ではありません。Java の標準ライブラリ全体で積極的に使われています。いくつか例を挙げます。
- Comparable<T> — オブジェクトを比較するためのインターフェース(たとえばソート時)。
- Runnable — スレッドを起動するためのインターフェース。
- Serializable — オブジェクトがシリアライズ可能であることを示すマーカーインターフェース。
- List、Set、Map — コレクションのインターフェース。
例: Comparable
public class Person implements Comparable<Person> {
String name;
int age;
// コンストラクターやその他のメソッド...
@Override
public int compareTo(Person other) {
return this.age - other.age;
}
}
これで、Person オブジェクトは Comparable を実装しているため、ソートできます。
6. ビジュアル図: インターフェースはどのように機能するか
+-------------------+ +-------------------+
| interface | | class |
| Movable |<--------| Robot |
|-------------------| |-------------------|
| +move(int, int) | | +move(int, int) |
+-------------------+ +-------------------+
- 矢印は、クラス Robot がインターフェース Movable を実装していることを示しています。
- インターフェースは「何が必要か」だけを定義し、クラスは「どのように動作するか」を定義します。
身近な例え
Java のインターフェースは運転免許のようなものです。もしあなたが免許(「運転者」インターフェースを実装)を持っているなら、車を運転できるということです。具体的にどう運転するかは人それぞれです。丁寧に運転する人もいれば、速く運転する人もいます。しかし重要なのは、「運転できる」と約束している点です。
7. 実用例: アプリを拡張する
public interface Movable {
void move(int x, int y);
}
public class Animal implements Movable {
protected String name;
public Animal(String name) {
this.name = name;
}
@Override
public void move(int x, int y) {
System.out.println(name + " は座標 (" + x + ", " + y + ") に移動します");
}
}
public class Robot implements Movable {
private String model;
public Robot(String model) {
this.model = model;
}
@Override
public void move(int x, int y) {
System.out.println("ロボット " + model + " が座標 (" + x + ", " + y + ") に向かって進みます");
}
}
public class Main {
public static void main(String[] args) {
Movable[] movables = {
new Animal("Barsik"),
new Robot("R2D2")
};
for (Movable m : movables) {
m.move(5, 10);
}
}
}
結果:
Barsik は座標 (5, 10) に移動します
ロボット R2D2 が座標 (5, 10) に向かって進みます
Movable 型のオブジェクト配列を作成しました。そこには、このインターフェースを実装する任意のオブジェクトを入れられます! ループ内のコードは、相手が動物かロボットかを知りませんが、どちらも move できることは分かっています。
8. インターフェースでよくあるミス
ミス No. 1: インターフェースのオブジェクトを直接作ろうとする。 インターフェースは契約であって、具体的なオブジェクトではありません。new Movable() のように書くことはできず、コンパイルエラーになります。インターフェースを実装したクラスのオブジェクトを作成する必要があります。
ミス No. 2: インターフェースのすべてのメソッドを実装していない。 クラスがインターフェースを実装しているのに、すべてのメソッドを実装していない場合、コンパイラは「Class must either be declared abstract or implement abstract method ...」というエラーを出します。すべて実装したくないなら、クラスを abstract として宣言してください。
ミス No. 3: アクセス修飾子を忘れる。 インターフェースのメソッドは常に public(明示しなくても)です。実装クラスではアクセスレベルを下げることはできないため、メソッドも public にする必要があります。
ミス No. 4: 通常のフィールドをインターフェースに追加しようとする。 インターフェースで宣言できるのは定数(public static final)のみです。通常(非 static)のフィールドは追加できません。
GO TO FULL VERSION