抽象クラス ちゅうしょうくらす
簡単に言うとこんな感じ!
「乗り物」って概念は分かるけど、「乗り物そのもの」を作ることはできないよね?車・バス・自転車という具体的な形にして初めて使えるんだ。抽象クラスはまさにそれで、「共通のルールと骨組みだけ定めた設計書」のことだよ!
抽象クラスとは
抽象クラス(Abstract Class)とは、オブジェクト指向プログラミングにおいて、直接インスタンス(実体)を生成できない、設計のひな形となるクラスのことです。「このクラスを継承する子クラスは、こういうメソッドを必ず実装しなければならない」というルールを強制しながら、共通の処理はまとめて定義しておけるのが特徴です。
抽象クラスには 「抽象メソッド(Abstract Method)」 を含めることができます。抽象メソッドとは、メソッドの名前と引数の定義だけがあって、中身(処理の実装)が書かれていないメソッドのこと。子クラス側で必ず実装を書かなければいけない、いわば「空欄の問題用紙」のような存在です。一方で、実装済みのメソッドも同時に持てるため、インターフェースとは異なり「共通処理の共有」もできるのが大きな強みです。
ビジネス的な視点で言えば、抽象クラスは「仕様書の標準テンプレート」に近いイメージです。プロジェクトごとに必ず記載すべき項目(抽象メソッド)を強制しつつ、どのプロジェクトでも同じ共通文言(実装済みメソッド)は使い回せる。チーム開発での品質統一や保守性向上に直結する重要な概念です。
抽象クラスの構造と役割
抽象クラス・具象クラス・インターフェースの比較
| 特徴 | 抽象クラス | 具象クラス | インターフェース |
|---|---|---|---|
| インスタンス生成 | ❌ できない | ✅ できる | ❌ できない |
| 実装済みメソッド | ✅ 持てる | ✅ 持てる | △ default実装のみ(言語による) |
| 抽象メソッド | ✅ 持てる | ❌ 持てない | ✅ 持てる(全メソッドが対象) |
| フィールド(状態) | ✅ 持てる | ✅ 持てる | ❌ 基本持てない |
| 継承できる数 | 1つのみ(多重継承不可) | 1つのみ | 複数実装できる |
| 主な用途 | 共通処理の共有+ルール強制 | 実際に使うオブジェクト | ルールのみ強制 |
抽象クラスの典型的な構造(Java風の例)
┌────────────────────────────────────┐
│ abstract class Animal │ ← 抽象クラス(直接は使えない)
│ │
│ ✅ void breathe() { ... } │ ← 実装済み(共通処理)
│ ❌ abstract void sound(); │ ← 抽象メソッド(中身なし)
│ ❌ abstract void move(); │ ← 抽象メソッド(中身なし)
└──────────────┬─────────────────────┘
│ 継承(extends)
┌────────┴────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ class Dog │ │ class Bird │ ← 具象クラス(実際に使える)
│ │ │ │
│ sound(){"ワン"}│ │ sound(){"チュン"}│ ← 必ず実装が必要
│ move(){走る} │ │ move(){飛ぶ} │ ← 必ず実装が必要
└──────────────┘ └──────────────┘
覚え方:「幽霊設計図」と覚える!
抽象クラスは「実体のない幽霊のような設計図」と覚えましょう。幽霊(抽象クラス)は直接触れないけど、確かにそこに存在して子孫(子クラス)に影響を与える。インスタンスを生成しようとすると「幽霊を捕まえようとしてもつかめない=コンパイルエラー」!というイメージです。
抽象クラスが活きる場面
【発注システムの例】
abstract class 注文処理 {
// 共通処理(実装済み)
void ログ記録() { ... }
void 在庫確認() { ... }
// チャネルごとに異なる処理(抽象)
abstract void 決済処理();
abstract void 通知送信();
}
class EC注文処理 extends 注文処理 { ... } // クレカ決済・メール通知
class 店舗注文処理 extends 注文処理 { ... } // 現金決済・レシート印刷
class 電話注文処理 extends 注文処理 { ... } // 代引き決済・SMS通知
在庫確認やログ記録は全チャネル共通なので親クラスにまとめ、決済と通知はチャネルごとに異なるので抽象メソッドとして強制実装させる——これが抽象クラスの真骨頂です。
歴史と背景
- 1960年代:Simula 67(世界初のオブジェクト指向言語)でクラスと継承の概念が登場。この時点でのクラス設計が後の抽象クラスの原型となる
- 1970〜80年代:Smalltalk・C++が登場し、クラス階層の設計パターンが実務で洗練されていく。C++では純粋仮想関数(
= 0)として抽象クラスに相当する機能が実装される - 1990年代:Javaが登場し、
abstractキーワードとして抽象クラスが言語仕様に明示的に組み込まれる。同時にインターフェースも導入され、両者の使い分けが議論されるように - 2000年代以降:GoF(Gang of Four)のデザインパターン(Template MethodパターンやFactory Methodパターン)が普及し、抽象クラスを活用した設計手法がエンタープライズ開発の標準になる
- 現在:Python・Kotlin・Swift・C#など主要言語が抽象クラスをサポート。型安全性と再利用性を両立させる基盤として、現代のフレームワーク設計に欠かせない概念となっている
抽象クラスとインターフェースの使い分け
抽象クラスとよく混同される概念がインターフェースです。どちらも「実装を強制する設計図」ですが、目的と使い方が異なります。
実務でどちらを選ぶべきか?
抽象クラスを選ぶとき:
- 複数の子クラスで「共通の処理(コード)」を使い回したい
- 「犬は動物の一種」のような is-a関係 がある
- 子クラスに共通の状態(フィールド)を持たせたい
インターフェースを選ぶとき:
- 「飛べる」「印刷できる」など能力・機能の契約だけを定めたい
- 全く異なる種類のクラスに同じルールを課したい(多重実装が必要)
- Javaなら8以降の
defaultメソッドで部分的に実装も可能になった
Template Methodパターン:抽象クラスの代表的活用法
抽象クラスが最も活きるデザインパターンが Template Method(テンプレートメソッド)パターンです。処理の「骨格(順序)」を親クラスに定義し、各ステップの実装を子クラスに委ねる設計手法です。
abstract class レポート生成 {
// テンプレートメソッド(処理の骨格を定義)
final void 生成する() {
ヘッダー出力(); // ← 共通処理(実装済み)
本文出力(); // ← 抽象メソッド(子クラスが実装)
フッター出力(); // ← 共通処理(実装済み)
}
abstract void 本文出力();
}
class 売上レポート extends レポート生成 {
void 本文出力() { /* 売上グラフを出力 */ }
}
class 在庫レポート extends レポート生成 {
void 本文出力() { /* 在庫一覧を出力 */ }
}
関連用語
- ./046-class.md — クラス:オブジェクトの設計図。抽象クラスもクラスの一種
- ./048-interface.md — インターフェース:実装を持たない純粋な契約・ルール定義
- ./049-inheritance.md — 継承:親クラスの性質を子クラスが引き継ぐ仕組み
- ./050-polymorphism.md — ポリモーフィズム:同じ操作で異なる動作をする仕組み。抽象クラスが支える核心概念
- ./051-concrete-class.md — 具象クラス:抽象クラスを継承してすべてのメソッドを実装した、実際に使えるクラス
- ./052-design-pattern.md — デザインパターン:Template MethodなどGoFパターンで抽象クラスが活躍
- ./053-encapsulation.md — カプセル化:内部実装を隠す概念。抽象クラスとともにOOPの基本原則
- ./045-oop.md — オブジェクト指向プログラミング:抽象クラスが属するプログラミングパラダイムの全体像