CodeGym /コース /JAVA 25 SELF /インターフェースと抽象クラスの違い

インターフェースと抽象クラスの違い

JAVA 25 SELF
レベル 20, レッスン 1
使用可能

1. 抽象クラス: 基礎の復習

比較に進む前に、抽象クラスとは何かをおさらいしましょう。

抽象クラスは、直接インスタンス化できないクラスです(new Animal() のようには書けません)。実装を持つ通常のメソッドと、実装を持たない抽象メソッドの両方を含むことができます。抽象クラスは、ほかのクラスの土台として使われ、振る舞いを継承させたり、いくつかのメソッドの実装を義務づけたりするのに用いられます。

たとえば、アプリにさまざまな種類の乗り物があるなら、抽象クラス Transport を用意できます。

public abstract class Transport {
    private String model;

    public Transport(String model) {
        this.model = model;
    }

    public String getModel() {
        return model;
    }

    // 抽象メソッド — 実装はなく、宣言だけ
    public abstract void move();

    // 通常のメソッド — 実装あり
    public void printInfo() {
        System.out.println("乗り物のモデル: " + model);
    }
}

抽象クラスの特徴:

  • フィールド(状態)を持てる。
  • 実装済みメソッドを持てる。
  • 抽象メソッドを持てる(サブクラスでの実装が必須)。
  • 直接インスタンス化できない。
  • 共通の振る舞いと状態を提供するために使われる。

2. インターフェース: 基礎の復習

インターフェースは、クラスが実装すべきメソッド群を定義するものです。インターフェースは状態を記述しません(通常のフィールドは持てず、定数のみ)。また(Java 8 以前は)メソッドの実装を含みません。インターフェースは純粋な契約であり、「これを実装するなら、この機能を持っていること」という約束です。

インターフェースの例:

public interface Movable {
    void move(int x, int y);
}

インターフェースの特徴:

  • 状態は持たない(public static final な定数のみ)。
  • Java 8 以前は抽象メソッドのみ(Java 8 以降は default メソッドと static メソッドが追加。詳細は後述)。
  • メソッドは既定で常に public abstract。
  • クラスは複数のインターフェースを実装できる。
  • クラスが「何ができるか」を表現するために使われる。

3. 比較表: 抽象クラス vs インターフェース

両者を正面から比較してみましょう。以下の表にまとめます。

特徴 抽象クラス インターフェース
構文
abstract class
interface
インスタンスを生成できるか? いいえ いいえ
通常メソッドを含められるか? はい Java 8 以前は不可、Java 8 以降は default/static のみ
抽象メソッドを含められるか? はい はい(Java 8 以前はすべて抽象メソッド)
フィールド(状態)を持てるか? はい(任意のフィールド) public static final(定数)のみ
コンストラクタを持てるか? はい いいえ
継承 抽象/通常クラスは1つだけ継承可能 複数のインターフェースを実装可能
メソッドの修飾子 任意(public、protected、private) メソッドは既定で public abstract。Java 9 以降はインターフェース内部で使う private メソッドを追加可能
継承の書き方
extends
implements
主な用途 共通の実装と状態 能力・役割の記述
JDK の例
AbstractList, AbstractMap
Comparable, Runnable

4. いつインターフェースを使い、いつ抽象クラスを使うか?

インターフェースを使う場面:

  • 「どのように実現するか」には関与せず、クラスが「何ができるか」だけを表現したいとき。
  • クラスに複数の独立した能力を持たせたいとき。
  • 例: Comparable(比較できる)、Serializable(シリアライズできる)、Runnable(スレッドで実行できる)。

抽象クラスを使う場面:

  • すべてのサブクラスに共通の実装と状態を提供したいとき。
  • すべてのサブクラスが特定のフィールドや実装付きメソッドを持つべきとき。
  • 継承は厳密に「1 対 1」(クラスは 1 つのクラス(通常または抽象)だけを継承)である必要があるとき。

身近なアナロジー

  • インターフェースは「運転免許証」のようなもの。持っていれば車を運転できるということですが、どの車でどう運転するかまでは規定しません。
  • 抽象クラスは「車の共通設計図」のようなもの。どの車にもハンドルやペダル、エンジンはありますが、各メーカーは詳細を独自に実装します。

5. Java 標準ライブラリからの例

インターフェース: Comparable

public interface Comparable<T> {
    int compareTo(T o);
}

このインターフェースを実装するクラスは、メソッド compareTo を実装しなければなりません。たとえば、String、Integer、LocalDate など多数があります。

抽象クラス: AbstractList

public abstract class AbstractList<E> implements List<E> {
    // いくつかの List メソッドのデフォルト実装
    // 一部のメソッドは抽象のまま
}

AbstractList は、コレクションの一部の振る舞い(たとえば追加/削除など)をすでに実装していますが、いくつかのメソッドは抽象のまま残し、サブクラスが独自に実装できるようにしています。

6. コード例: 実践的な比較

インターフェース

インターフェースと、それを実装するクラスを作成します。

public interface Printable {
    void print();
}

public class Document implements Printable {
    @Override
    public void print() {
        System.out.println("ドキュメントを印刷しています...");
    }
}

抽象クラス

次に、抽象クラスとそのサブクラスです。

public abstract class Machine {
    public void turnOn() {
        System.out.println("マシンが起動しました。");
    }

    public abstract void work();
}

public class Printer extends Machine {
    @Override
    public void work() {
        System.out.println("プリンターが印刷しています...");
    }
}

クラスが両方に対応: インターフェースと抽象クラス

public class SmartPrinter extends Machine implements Printable {
    @Override
    public void work() {
        System.out.println("スマートプリンターが動作しています...");
    }

    @Override
    public void print() {
        System.out.println("スマートプリンターが印刷しています...");
    }
}

7. インターフェースの多重実装: これが強力な理由

Java では、クラスは 1 つのクラス(抽象/通常)しか継承できませんが、インターフェースは好きなだけ実装できます。これにより、柔軟で拡張可能なアーキテクチャを構築できます。

public interface Scannable {
    void scan();
}

public class MultiFunctionPrinter extends Machine implements Printable, Scannable {
    @Override
    public void work() {
        System.out.println("複合機が動作しています...");
    }

    @Override
    public void print() {
        System.out.println("複合機が印刷しています...");
    }

    @Override
    public void scan() {
        System.out.println("複合機がスキャンしています...");
    }
}

どちらを選ぶべきか?

  • 共通の状態(フィールドなど)を持つ基本的な機能を設計するなら、抽象クラスを使いましょう。
  • オブジェクトに「できることのラベル」(例: 「印刷できる」「比較できる」「シリアライズできる」)を付与したいなら、インターフェースを使いましょう。
  • 迷ったら、まずはインターフェースから始めるのがよい習慣です。インターフェースは柔軟性と拡張性を高めます。

8. よくあるミスと落とし穴

誤り1: 複数のクラスを継承しようとする — Java は許しません!
クラスが継承できるクラスは 1 つだけですが、インターフェースは多数実装できます。たとえば、class A extends B, C はエラーですが、class A extends B implements X, Y, Z は問題ありません。

誤り2: インターフェースのフィールドとクラスのフィールドを混同する。
インターフェースで宣言できるのは定数(public static final)のみです。たとえば private int count; のような通常の状態は宣言できず、コンパイラがすぐにエラーにします。

誤り3: インターフェースの全メソッドを実装していない。
クラスがインターフェースのメソッドを 1 つでも実装していない場合、そのクラスは abstract として宣言しなければなりません。そうでないとコンパイルエラーになります。

誤り4: インターフェースや抽象クラスのインスタンスを生成しようとする。
どちらも「未完成の型」です。拡張はできますが、直接インスタンス化することはできません。

Printable p = new Printable(); // エラー!
Machine m = new Machine();     // エラー!

誤り5: インターフェースにコンストラクタがあると思っている。
インターフェースにはコンストラクタはありません。オブジェクトの状態を記述しないためです。コンストラクタを持てるのはクラス(通常クラスと抽象クラス)だけです。

1
タスク
JAVA 25 SELF, レベル 20, レッスン 1
ロック未解除
輸送会社:統一移動プロトコル
輸送会社:統一移動プロトコル
1
タスク
JAVA 25 SELF, レベル 20, レッスン 1
ロック未解除
大学レジストリ:個人と識別子
大学レジストリ:個人と識別子
1
タスク
JAVA 25 SELF, レベル 20, レッスン 1
ロック未解除
多機能オフィス機器
多機能オフィス機器
1
タスク
JAVA 25 SELF, レベル 20, レッスン 1
ロック未解除
スマートフォン: 共通デバイスからパーソナルガジェットへ
スマートフォン: 共通デバイスからパーソナルガジェットへ
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION