Javaのインターフェース(interface)とは、クラスがどのような機能を提供するかを定義するための「仕様書」のようなもの。
インターフェースを使うことで、異なるクラス間で一貫性のある機能を提供することができるようになります。例えば、複数のクラスに共通するメソッドをインターフェースで定義することで、クラスごとに異なる実装を行いながらも、同じメソッドを持つことが保証されます。これにより、コードの再利用性やメンテナンス性が向上し、大規模なプロジェクトでも柔軟で拡張性のある設計が可能になります。
と、言葉だけで説明されてもピンと来ない方も多いはず。このページでは実際のコードを見ていきながら、インターフェースの基本と使い方のコツを1からわかりやすく解説します。
関連 Javaの1stステップ:基本的な構文ルールを1分で復習!
インターフェースとは?
Javaのインターフェースとは、クラスが実装すべきメソッドの仕様を定義するものです。具体的なメソッドの実装は含まず、メソッドの名前、引数、戻り値の型のみを記述します。
早速具体的な構文ルールを確認しつつ、実際のイメージをつかんでいきましょう。
インターフェースの定義:interface
まず、インターフェースを定義するには、interface
キーワードを使用します。以下は、動物が鳴き声を出す機能を定義したインターフェースの例です。
public interface Animal { void makeSound(); }
このインターフェースAnimal
は、makeSound
というメソッドを持つことを定義しています。が、しかし、この時点ではメソッドの実装(具体的な動作)は含まれていません。
インターフェースの実装:implement
インターフェースを実装するには、クラスにimplements
キーワードを使用します。以下は、Animal
インターフェースを実装したDog
クラスの例です。
public class Dog implements Animal { public void makeSound() { System.out.println("Woof"); } }
上記の通り、Dog
クラスはAnimal
インターフェースを実装し、makeSound
メソッドの具体的な動作を定義しています。同様に、他の動物クラスも同じインターフェースを実装することができます。
public class Cat implements Animal { public void makeSound() { System.out.println("Meow"); } }
複数のインターフェースの実装
Javaでは、クラスが複数のインターフェースを実装することも可能です。以下は、動物が鳴き声を出し、かつ移動する機能を持つインターフェースを実装した例です。
public interface Movable { void move(); } public class Dog implements Animal, Movable { public void makeSound() { System.out.println("Woof"); } public void move() { System.out.println("Running"); } }
Dog
クラスはAnimal
とMovable
の両方のインターフェースを実装することができます。
なぜインターフェースを利用するのか
なぜインターフェースを利用するのか?
その答えの1つはコードの一貫性を保つためです。インターフェースを使用することで、異なるプログラマーが作成したクラスでも、同じメソッドを持つことが保証されます。例えば、動物を扱うプログラムを作成するときに、すべての動物クラスがmakeSound()
というメソッドを持つようにすることができます。これにより、どの動物クラスでも一貫して鳴き声を出す機能を提供することが可能になります。
// インターフェースの定義 public interface Animal { void makeSound(); } // インターフェースの実装 // 必ずmakeSound()を実装していることが保証される public class Dog implements Animal { public void makeSound() { System.out.println("Woof"); } } public class Cat implements Animal { public void makeSound() { System.out.println("Meow"); } }
もう1つの理由は、柔軟性と拡張性を高めるためです。インターフェースを使用するこで、例えば新しい機能を追加する際にも既存のコードを変更する必要がなくなります。
先ほどと同様、動物の鳴き声を扱うプログラムを考えます。最初にAnimal
というインターフェースを定義し、makeSound()
というメソッドを持つことを決めます。
public interface Animal { void makeSound(); }
次に、Dog
とCat
というクラスを作り、それぞれがAnimal
インターフェースを実装します。
public class Dog implements Animal { public void makeSound() { System.out.println("Woof"); } } public class Cat implements Animal { public void makeSound() { System.out.println("Meow"); } }
ここで、動物園のプログラムで動物の鳴き声を再生する機能を実装します。
public class Zoo { public void makeAnimalSound(Animal animal) { animal.makeSound(); } public static void main(String[] args) { Animal dog = new Dog(); Animal cat = new Cat(); Zoo zoo = new Zoo(); zoo.makeAnimalSound(dog); // Woof zoo.makeAnimalSound(cat); // Meow } }
もしここで、新しい動物クラスを追加したい場合、たとえばBird
クラスを追加する場合を考えてみましょう。このとき、Animal
インターフェースを実装するだけで、新しいクラスを既存のコードに簡単に組み込むことができます。
public class Bird implements Animal { public void makeSound() { System.out.println("Tweet"); } }
このBird
クラスを追加しても、Zoo
クラスのコードを変更する必要はありません。既存のmakeAnimalSound()
メソッドをそのまま使うことが可能です。
public class Zoo { public void makeAnimalSound(Animal animal) { animal.makeSound(); } public static void main(String[] args) { Animal dog = new Dog(); Animal cat = new Cat(); Animal bird = new Bird(); // 新しいBirdクラスを追加 Zoo zoo = new Zoo(); zoo.makeAnimalSound(dog); // Woof zoo.makeAnimalSound(cat); // Meow zoo.makeAnimalSound(bird); // Tweet } }
このように、新しい動物クラスを追加する際に、Zoo
クラスのコードに全く変更を加える必要がないことがわかります。Zoo
クラスはAnimal
インターフェースを使って動物を扱っているため、新しい動物クラスが追加されても既存の仕組みを壊さずに動作します。これがインターフェースの利用が拡張性を高める一例です。
まとめとして、日常生活の例を使って説明をしてみると、インターフェースは「スマホの充電器」のようなものです。
たとえば、充電器の端子がUSB-Cで統一されているとします。新しいスマホを買ったときそのスマホがUSB-C端子を使っていれば、既存の充電器をそのまま使うことができますよね。この結果、あなたが新しいスマホを追加しても、再度充電環境を変える必要はなくなります。同じ充電器で異なるスマホを充電できるため、既存の設備を維持しつつ新しいデバイスを使えますよ!というのがインターフェースのメリットです。