ホーム › オブジェクト指向設計(OOP) › OOP-01
OOP-01: インターフェース vs 抽象クラスの使い分け
インターフェースと抽象クラスはどちらも「共通の型を定義する仕組み」ですが、 使い分けの判断基準を理解することで、より柔軟で保守しやすい設計ができます。
インターフェース vs 抽象クラス — 何が違うのか
一言で表すと、インターフェースは「何ができるか(契約・能力)」を定義し、 抽象クラスは「何者か(共通の実装・状態)」を定義します。
| 項目 | インターフェース | 抽象クラス |
|---|---|---|
| 多重実装 | 複数実装できる | 1クラスにつき1つだけ継承 |
| フィールド | 持てない(定数のみ) | インスタンス変数を持てる |
| コンストラクタ | 持てない | 持てる |
| 共通実装 | default メソッド(Java 8+) | 通常のメソッドとして定義 |
| 主な用途 | DAO・Strategy・Observer パターン | Template Method パターン |
使い分けの判断基準
- インターフェース: 実装を差し替えたい(テスト用のモック、複数の DB 実装など)。複数の「能力」を持たせたい(Printable かつ Saveable など)。
- 抽象クラス: 共通の状態(フィールド)や実装を持つクラス群をまとめたい。Template Method パターン(処理の骨格を定義してサブクラスに詳細を委ねる)を使いたい。
サンプルコード
Java 8 からインターフェースに default メソッドと static メソッドを定義できるようになりました。これにより、インターフェースに共通の処理を持たせることが可能になっています。
よくあるミス・注意点
⚠️ すべてを抽象クラスで書いて単一継承の制限にはまる
Java は1つのクラスから1つのクラスしか継承できません(単一継承)。 そのため「Animal を継承した Dog はすでに別のクラスを継承できない」という制約が生まれます。 「何ができるか(能力)」を表現する場合は、インターフェースを使うと複数の能力を持たせられます。 例えば Dog extends Animal implements Printable, Saveable のように、 抽象クラスによる「何者か」の定義とインターフェースによる「能力」の定義を組み合わせるのが典型的なパターンです。
⚠️ Java 8 以前の感覚で default メソッドを知らずにユーティリティクラスを乱用する
Java 8 よりも前は、インターフェースに実装を持たせることができませんでした。 そのため、共通処理を XxxUtils のような static メソッドだけを持つユーティリティクラスに書くパターンが多く使われていました。 Java 8 以降はインターフェースに default メソッドを定義できるため、 共通の振る舞いをインターフェース自体に持たせることができます。 ただし、default メソッドは「デフォルトの実装」であり、実装クラスでオーバーライドして変更することもできます。
テストする観点
- 同じインターフェース(
UserDao)の実装を差し替えても、呼び出し側のコードが変わらないこと(DAO パターンの確認) - テスト時に
InMemoryUserDaoに差し替えて、外部 DB なしでテストできること - default メソッド(
saveToTemp())が正しく動作し、サブクラスでオーバーライドできること - 抽象クラスのコンストラクタ(
Animal(String name))が継承先でも正しく呼ばれること - Dog が Printable と Saveable の両方のインターフェースを正しく実装していること(キャスト確認)