Javaのインタフェース完全解説!初心者が知るべき基本ルールと使い方
生徒
「インタフェースって何ですか?抽象クラスとはどう違うんでしょうか?」
先生
「インタフェースは、クラスが実現すべき『型』を定義するものです。抽象クラスとは違い、インタフェースでは全てのメソッドが抽象メソッドになります。詳しく見ていきましょう!」
1. インタフェースとは?
インタフェースは、クラスが実現すべき機能を定義する契約のようなものです。インタフェースを使うことで、異なるクラスでも共通の操作を提供できます。インタフェースはinterfaceキーワードを使って定義します。
基本的な構文は以下の通りです:
public interface MyInterface {
void sayHello(); // 抽象メソッド
}
インタフェースには、publicな抽象メソッドを定義します。アクセス修飾子を省略しても、暗黙的にpublicとみなされます。
2. インタフェースの実現
インタフェースを実現するには、implementsキーワードを使用します。実現したクラスは、インタフェースに定義されたすべてのメソッドをオーバーライドしなければなりません。
以下は、インタフェースを実現した例です:
public interface MyInterface {
void sayHello();
}
public class MyClass implements MyInterface {
@Override
public void sayHello() {
System.out.println("Hello, World!");
}
}
public class Main {
public static void main(String[] args) {
MyInterface obj = new MyClass();
obj.sayHello(); // 出力: Hello, World!
}
}
このコードでは、MyClassがMyInterfaceを実現し、sayHelloメソッドをオーバーライドしています。
3. インタフェースの特徴と制限
インタフェースには以下の特徴と制限があります:
- フィールドは
public static final(定数)としてのみ定義可能。 - すべてのメソッドは暗黙的に
public abstractとみなされる。 - インタフェース同士の多重継承が可能。
以下のコードで定数を使う例を確認してください:
public interface Constants {
int MAX_VALUE = 100; // 暗黙的にpublic static final
}
public class Example implements Constants {
public void printValue() {
System.out.println("Max value is: " + MAX_VALUE);
}
}
4. 抽象クラスとインタフェースの違い
抽象クラスとインタフェースは似ていますが、以下の違いがあります:
- 抽象クラスは、フィールドや具体的なメソッドを持つことができる。
- インタフェースでは、メソッドの実装を持つことはできない。
- クラスは1つの抽象クラスしか継承できないが、複数のインタフェースを実現できる。
以下のコードで違いを確認してください:
public abstract class AbstractClass {
abstract void displayMessage();
public void concreteMethod() {
System.out.println("This is a concrete method.");
}
}
public interface MyInterface {
void sayHello();
}
public class Example extends AbstractClass implements MyInterface {
@Override
void displayMessage() {
System.out.println("Displaying message.");
}
@Override
public void sayHello() {
System.out.println("Hello from interface.");
}
}
5. 試験対策ポイント
- インタフェースは抽象メソッドのみを定義する。
- インタフェースのフィールドはすべて定数であり、初期値が必要。
- インタフェースの多重実現を活用する。
6. 多重実現(implements)で設計を柔軟にする
Javaのインタフェースは、クラスが複数のインタフェースを同時に実現できる点が大きな特徴です。 抽象クラスは1つしか継承できませんが、インタフェースなら役割ごとに分割して組み合わせられるため、 機能追加や仕様変更に強い設計を作りやすくなります。
次の例では、2つのインタフェースを1つのクラスで実現しています:
public interface Printable {
void print();
}
public interface Savable {
void save();
}
public class Document implements Printable, Savable {
@Override
public void print() {
System.out.println("Printing document...");
}
@Override
public void save() {
System.out.println("Saving document...");
}
}
public class Main {
public static void main(String[] args) {
Document doc = new Document();
doc.print();
doc.save();
}
}
このようにインタフェースを分けて設計すると、必要な機能だけを組み合わせられるため、クラスの責務を整理しやすくなります。
7. インタフェース型で扱うメリット(ポリモーフィズム)
インタフェースを学ぶうえで重要なのは、実装クラスそのものではなく、インタフェース型で変数を持つという考え方です。 これにより、呼び出し側は「どのクラスが実装しているか」を意識せずに共通の操作を呼び出せます。 結果として、実装の差し替えが容易になり、保守性の高いコードにつながります。
次の例では、同じMyInterface型で複数の実装を扱っています:
public interface MyInterface {
void sayHello();
}
public class EnglishHello implements MyInterface {
@Override
public void sayHello() {
System.out.println("Hello!");
}
}
public class JapaneseHello implements MyInterface {
@Override
public void sayHello() {
System.out.println("こんにちは!");
}
}
public class Main {
public static void main(String[] args) {
MyInterface a = new EnglishHello();
MyInterface b = new JapaneseHello();
a.sayHello();
b.sayHello();
}
}
このようにインタフェース型で統一しておくと、新しい実装クラスを追加しても呼び出し側の書き方を揃えやすくなります。
8. ありがちなミスと理解のポイント
インタフェースはシンプルに見えますが、初心者が混乱しやすい点もあります。 とくに「暗黙的にそうなるルール」を押さえておくと、試験対策や実装時のミスを減らせます。
- インタフェースのメソッドは、修飾子を省略しても暗黙的に
public abstractとして扱われる。 - インタフェースのフィールドは、暗黙的に
public static finalとなり、必ず初期値が必要。 - 実現クラスは、インタフェースのメソッドを
publicでオーバーライドしないとコンパイルエラーになる。
次の例では、メソッドのアクセス修飾子が原因でエラーになるケースを示します:
public interface MyInterface {
void sayHello(); // 暗黙的にpublic abstract
}
public class Example implements MyInterface {
// void sayHello() { } // コンパイルエラー(publicが必要)
@Override
public void sayHello() {
System.out.println("Hello!");
}
}
インタフェースは「共通の操作を揃えるための型」なので、公開範囲が揃っていることが前提になります。 ルールを意識しておくと、インタフェースの使い分けが一気に理解しやすくなります。
まとめ
今回の記事では、Javaのインタフェース(interface)について、基礎ルールから実用例までを整理しました。
インタフェースは「クラスが実現すべき契約(API)」を定義する仕組みであり、実装側(implementsするクラス)と利用側(呼び出し側)を分離できるため、
設計の柔軟性や拡張性を高めるうえで重要です。とくに、複数のクラスに共通の操作を持たせたいときに効果を発揮します。
初心者がつまずきやすいポイントとして、インタフェースのメソッドは基本的にpublic abstractとして扱われること、
そしてフィールドはpublic static finalの定数としてのみ定義できる点が挙げられます。
また、抽象クラスとの違い(目的・使い分け・継承/実現の関係)を明確に理解すると、
「共通処理を持たせたいのか」「共通の型(振る舞い)を揃えたいのか」を判断しやすくなり、適切な設計ができるようになります。
以下に、インタフェースの利用例を改めて示します(Animalを共通の型として扱い、実装ごとに振る舞いを差し替えています)。
public interface Animal {
void makeSound();
}
public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
dog.makeSound(); // 出力: Woof!
cat.makeSound(); // 出力: Meow!
}
}
この例では、Animalインタフェースを複数のクラスが実現し、それぞれの具体的な振る舞いを提供しています。
重要なのは、呼び出し側がDogやCatといった具体クラスではなく、Animalという共通の型で扱える点です。
これにより、新しい動物クラス(例:Birdなど)を追加しても、同じ操作(makeSound)を共通インタフェースとして呼び出せるため、
コードの再利用性が高まり、変更に強い設計になります。
生徒
「インタフェースを使うと、どんなときに便利なんですか?」
先生
「例えば、異なる種類のオブジェクトに共通の操作を持たせたい場合に便利です。
さっきの例では、犬も猫もmakeSoundという操作を持つので、呼び出し側は“動物の種類”を意識せずに同じ書き方で使えましたね。
つまり、実装が変わっても利用コードを変えにくくできるのが大きなメリットです。」
生徒
「なるほど!抽象クラスと違って、複数のインタフェースを実現できるのも強みですね。」
先生
「その通りです。複数のインタフェースを実現(implements)できるので、設計の選択肢が増えます。 とくに大規模開発では、機能ごとに責務を分けてインタフェースを設計すると、拡張やテストもしやすくなりますよ。」