Java

Javaのインターフェイスを徹底解説!メリットと実践例で完全理解!

Javaのオブジェクト指向を学ぶ上で欠かせない「インターフェイス」。
でも、「インターフェイスって何?」と悩む初心者も多いはず。
本記事では、インターフェイスの基礎から具体例、メリット、抽象クラスとの違いまでを図解を交えて徹底解説します!
この記事を読めば、「なぜJavaでインターフェイスを使うのか」がスッキリ理解できるはずです。

Javaのインターフェイスを完全理解するために

Javaのオブジェクト指向を理解する上で、「インターフェイス」について理解することは避けて通れません。

ただ、Javaの初心者にとって、「インターフェイス」とは何かがいまひとつピンとこない場合が多いでしょう。その理由の一つとして、この「インターフェイス」をクラス設計に用いるメリットが明確に理解されていないことが挙げられます。

そこで本記事では下記の内容を中心に、「Javaのインターフェイスとは?」に関して図解付きで徹底解説!

  • Javaの「インターフェイス」とは
  • Javaの「インターフェイス」を用いるメリット
  • 「抽象クラス」と「インターフェイス」の違い

本記事を最後まで読んでいただくことで、その場だけの知識ではなく、「なぜJavaでインターフェイスを用いるのか」という深い理解につなげることができます。

punpee-code
punpee-code
「Javaでインターフェイスを用いるメリット」まで理解できるようになろう!

 

Javaのインターフェイスをわかりやすく解説!

インターフェイスの基本概念と活用例

Javaでの「インターフェイス」とは、実装関係にあるクラスが持つべきメソッドを定めたクラス設計方法です。

インターフェイスで抽象メソッドを宣言し、その具体性な処理は実装されたクラス側で完成させます。

これにより、実装関係にあるクラスの共通の型ができ、クラスを一元的に操作することを可能にします。

例:

// インターフェイスを定義
public interface Moveable {
    // 定数
    String msg = "移動手段"

    // 抽象メソッド
    void showDistance(); // ※ 暗黙的に「public abstract」
}

// スーパークラスを定義
class Transportation {
    String name; // メンバ変数

    // コンストラクタ
    Transportation(String name) {
        this.name = name;
    }
}

// 「実装クラス」かつ「サブクラス」であるクラスを定義
class Train extends Transportation implements Moveable {
    private int distance; // メンバ変数

    // コンストラクタ
    Train(String name, int distance) {
        super(name);
        this.distance = distance;
    }

    @Override
    // オーバーライド元と同様のpublicの修飾子である必要があるため、public指定必須
    public void showDistance() {
        System.out.println(Moveable.msg);
        System.out.println(name + " : " + distance + "km");
    }
}

 

インターフェイスで何が宣言できる?

  • 抽象メソッド(暗黙的に「public abstract」)
    • 「public」なのは、実装クラスにおいて具体性な処理内容がオーバーライドされる前提のため
  • 定数(暗黙的に「public static final」)
    • インスタンス変数は持たず、実装クラスで共通に使用する定数となる。final指定のため、宣言時に明示的な初期化が必要。
  • 以下の具象メソッド(※詳細は割愛します)
    • defaultメソッド(Java8以降)
    • staticメソッド(Java8以降)
    • privateメソッド(Java8以降)

 

インターフェイスの注意点まとめ

  • インターフェイス同士で継承することができる
  • 実装クラスにおいては、複数のインターフェイスを実装することができる
  • インターフェイスは実装(implements)される前提のため、finalにはできない
  • 実装クラスにおけるオーバーライドメソッドにおいては、オーバーライド元と同じ範囲の修飾子となる必要があるため、public指定必須
public interface Hoge {
    void sampleMethod();
}

// インターフェイス同士で継承することができる
public interface Fuga extends Hoge {}

// インターフェイスは複数を実装することができる
public class Piyo implements Hoge Fuga {}

// コンパイルエラー:実装(implements)される前提のため、finalにはできない
// public final interface Piyo {}

public class HogeHoge implements Hoge {
    @Override
    // オーバーライド元と同様のpublicの修飾子である必要があるため、public指定必須
    public void sampleMethod() {
        System.out.println("オーバーライドしたよ");
    }
}

 

Javaのインターフェイスを使うメリットとは?

重要なメソッドの実装漏れを防ぐ

インターフェースが持つ抽象メソッドは、実装クラスが「何をすべきか」を定義します。

実装の詳細には触れず、どのクラスでも同じメソッドを持つことを保証するため、重要なメソッドの実装漏れを防ぐことができます。

多重継承の代わりとなる

Javaではクラスの多重継承はできませんが、インターフェースは複数実装可能です。

異なる機能を持つインターフェースを組み合わせることで、柔軟な設計が可能になります。

柔軟性と拡張性の向上

インターフェースを1つ作ってしまえば、実装を変更しても外部に影響を与えません。

実装を差し替えたり、異なる実装を簡単に扱うことができます。

ポリモーフィズムの活用

ポリモーフィズム(多様性)」は、同じ操作の呼び出しで、オブジェクトごとに異なる動作を実現することです。オブジェクト指向において重要な概念になります。

例:

/**
 * あくまで、Fruitsクラスを継承or実装している各クラスが存在しており、
 * eatIt()というメソッドをそれぞれ異なる処理内容でオーバーライドしているという想定です。
 */
public class Main {
    public static void main(String[] args) {
        Fruits[] fruits= { new Apple(), new Banana(), new Mango(), new Grapes() };

        // f.eatIt()でポリモーフィズムを実現したメソッドアクセス
        // 配列内の各共通のeatIt()にアクセス(毎回のループで参照するオブジェクトが切り替わる)
        for (Fruits f : fruits) {
            System.out.println(f.eatIt());
        }
    }
}

 

あくまで想定の範囲ではありますが、eatIt()というメソッドをそれぞれ異なる処理内容でオーバーライドしているため、1つの呼び出しによって、異なる処理が行われる形になります。

よくある質問(FAQ)

Q: 「抽象クラス」と「インターフェイス」の違いは何ですか?

Javaにおいて「抽象クラス」と「インターフェース」は、どちらもクラスに共通の機能を持たせる仕組みですが、それぞれ目的や使い方が異なります。以下でその違いを具体例とともに解説します。

1. 定義と役割の違い

  • 抽象クラス:
    クラス間で共通の性質や動作をまとめるために使用します。
    一部のメソッドを具体的に実装できるため、サブクラスで共通の振る舞いを継承させることができます。
  • インターフェース:
    クラス間で共通のルールを定義します。
    実装の詳細には触れず、複数の異なるクラスで共通の振る舞いを保証することが目的です。

 

2. コード例で比較

【抽象クラスの例】
「動物」という概念を表すAnimal抽象クラスを定義します。このクラスはすべての動物が共有する**フィールド(名前)具体的な動作(sleep()メソッド)**を持ちつつ、鳴き声(makeSound()メソッド)はサブクラスに委ねます。

abstract class Animal {
    String name; // 共通のフィールド

    Animal(String name) {
        this.name = name;
    }

    // サブクラスで実装すべき抽象メソッド
    abstract void makeSound();

    // 具体的なメソッド
    void sleep() {
        System.out.println(name + " is sleeping");
    }
}

class Dog extends Animal {
    Dog(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " says: Woof!");
    }
}

使用例:

Animal dog = new Dog("Buddy");
dog.makeSound(); // 出力: Buddy says: Woof!
dog.sleep();     // 出力: Buddy is sleeping

 

【インターフェースの例】
「飛ぶ能力」を表すFlyableインターフェースを定義します。このインターフェースを実装するクラスには、fly()メソッドを必ず実装させます。

interface Flyable {
    // 抽象メソッド
    void fly();

    // デフォルトメソッド(Java 8以降)
    default void land() {
        System.out.println("Landing...");
    }
}

class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("Bird is flying");
    }
}

class Plane implements Flyable {
    @Override
    public void fly() {
        System.out.println("Plane is flying");
    }
}

使用例:

Flyable bird = new Bird();
bird.fly();  // 出力: Bird is flying
bird.land(); // 出力: Landing...

Flyable plane = new Plane();
plane.fly(); // 出力: Plane is flying

 

3. 違いを表で比較

抽象クラスインターフェース
主な役割共通の性質や動作を持たせる共通のルールを定義する
継承の制限単一継承のみ複数のインターフェースを実装可能
メソッド具体的・抽象的なメソッドを定義可能抽象メソッド、デフォルト・静的メソッドを定義可能
フィールドインスタンス変数を持てる定数(static final)のみ持てる
使用例共通の振る舞いを持つクラスの基盤異なるクラス間の共通の振る舞いを統一する

 

4. 使い分けのポイント

  • 抽象クラスを選ぶ場面
    • 共通のフィールド具体的なメソッドを持たせたいとき。
    • 継承階層でサブクラスの基本的な動作を定義したいとき。
  • インターフェースを選ぶ場面
    • 実装の詳細を隠しつつ、異なるクラスで共通のルールを実現したいとき。
    • クラスが複数の役割を持つ必要があるとき。(多重実装)

おわりに

まとめ
  • Javaの「インターフェイス」とは
    • 実装関係にあるクラスが持つべきメソッドを定めたクラス設計方法
  • Javaの「インターフェイス」を用いるメリット
    • 重要なメソッドの実装漏れを防ぐ
    • 多重継承の代わりとなる
    • 柔軟性と拡張性の向上
    • ポリモーフィズムの活用
  • 「抽象クラス」と「インターフェイス」の違い
    • 抽象クラス:共通の性質や動作を共有するのに適している。
    • インターフェイス:異なるクラス間で共通のルールを定めるために使います。