TypeScriptの抽象クラス(abstract)の定義と実装方法を徹底解説!初心者でも理解できるオブジェクト指向の基本
生徒
「先生、TypeScriptでabstractっていうキーワードを見かけたんですけど、これは何ですか?」
先生
「いい質問ですね。abstractは『抽象クラス』を作るときに使うキーワードです。抽象クラスは、共通の設計図のような役割を持ちます。」
生徒
「設計図ってどういうことですか?」
先生
「例えば、動物を表すクラスを考えるときに『必ず鳴くメソッドを持たせたい』と決められるのが抽象クラスです。実際の鳴き方は犬や猫などのサブクラスに任せるんです。」
生徒
「なるほど!つまり、抽象クラスは『共通ルール』を決めるんですね?」
先生
「そのとおりです。では、具体的な使い方を見てみましょう!」
1. 抽象クラス(abstract class)とは?
TypeScriptの抽象クラス(abstract class)とは、インスタンス(実体)を直接作ることができないクラスのことです。簡単に言うと、『設計図だけを定義して、実際の中身は子クラスで実装させる仕組み』です。
例えば、動物のクラスを作る場合、動物は必ず「鳴く」動作を持っているとします。しかし、犬と猫では鳴き方が違いますよね。このように共通するけど内容が異なる処理を定義する時に抽象クラスが使われます。
2. 抽象クラスの基本構文
抽象クラスはabstractキーワードを使って定義します。そして、クラスの中に抽象メソッドを宣言すると、子クラスは必ずそのメソッドを実装しなければなりません。
abstract class Animal {
// 抽象メソッド:中身は書かずに定義だけ
abstract makeSound(): void;
// 具体的なメソッドも書ける
move(): void {
console.log("動物が移動しました");
}
}
このようにabstractを付けたメソッドは、あくまで「ルール」だけを決めます。実際の処理は子クラスで書かなければなりません。
3. 抽象クラスを継承して実装する
抽象クラスを使うときは、必ず子クラスを作って実装します。犬と猫を例にしてみましょう。
class Dog extends Animal {
makeSound(): void {
console.log("ワンワン!");
}
}
class Cat extends Animal {
makeSound(): void {
console.log("ニャー!");
}
}
const dog = new Dog();
dog.makeSound(); // ワンワン!
dog.move(); // 動物が移動しました
const cat = new Cat();
cat.makeSound(); // ニャー!
cat.move(); // 動物が移動しました
ワンワン!
動物が移動しました
ニャー!
動物が移動しました
このように、DogとCatは共通して「makeSound」というメソッドを必ず実装するルールに従っています。これが抽象クラスの大きな特徴です。
4. 抽象クラスと通常のクラスの違い
普通のクラスは直接インスタンスを作ることができますが、抽象クラスはできません。
// エラーになる例
const animal = new Animal(); // NG: 抽象クラスはインスタンス化できない
抽象クラスはあくまで『ルール』や『共通の仕組み』を提供するだけで、具体的な中身は子クラスに任せます。
5. 抽象クラスを使うメリット
抽象クラスを使うメリットは以下の通りです。
- 共通ルールを強制できる:開発チームでコーディングする際に、同じ名前のメソッドを必ず持たせることができる。
- コードの再利用性が上がる:共通処理を抽象クラスに書いて、子クラスで使い回せる。
- プログラムの見通しが良くなる:設計図があることで、プロジェクト全体の構造が分かりやすくなる。
これは大規模なアプリケーションを作るときや、複数人で開発する時に特に役立ちます。
6. 具体的なイメージ例
抽象クラスを日常生活に例えると、「運転免許のルール」に似ています。例えば、自動車を運転するには必ず『ブレーキ』や『アクセル』が必要です。しかし、トヨタ車とホンダ車ではデザインや細かい仕組みは違いますよね。
抽象クラスは「車には必ずブレーキとアクセルがある」というルールを決める部分で、各メーカー(子クラス)はそれを実際にどう作るかを決めます。これにより、誰が作っても共通の仕組みで動かせるようになるのです。
まとめ
ここまで抽象クラスの考え方や具体的な実装方法を詳しく見てきましたが、あらためて整理してみると、抽象クラスはただのコードの書き方ではなく、ものごとの共通点を整理して全体の仕組みを分かりやすく保つための大切な考え方だと分かります。特に、複雑なアプリケーションを組み立てる場面では、コードの役割がはっきりしていないと途中で混乱しやすくなります。そこで抽象クラスを使うと、「これは必ず実装しないといけない」「これは共通で使える」というルールを明確に示せるので、プロジェクト全体の見通しがずっと良くなります。
抽象クラスは実際の処理を持たずに設計図として機能するため、作成する段階では少し抽象的に感じるかもしれません。しかし、子クラスが必ず実装すべきメソッドを定義したり、共通して利用する動作をまとめたりできるので、コードのまとまりが生まれます。また、複数のクラスが同じ名前のメソッドを持つことで、扱う側の視点からもコードを理解しやすくなり、機能の拡張が必要になったときにも柔軟に対応できるようになります。特にオブジェクト指向の学習に慣れていない初心者にとっては、「共通のパーツをひとつの場所に集合させる」という意識を持つ良い練習にもなります。
さらに、抽象クラスは継承と組み合わせてこそ本領を発揮します。継承を行うことで、子クラスが抽象クラスで定義されたルールに必ず従うようになり、どの子クラスを使っても同じ操作で扱えるようになります。これは例えば、動物の例であれば「必ず鳴く」というルールがどのクラスにも適用されるため、扱うコード側では特定の動物に依存する必要がなくなります。このように、抽象クラスはオブジェクト指向における統一性の軸を作る重要な役割を果たします。
サンプルコードでもう一度確認する
ここでは、抽象クラスを使った典型的なサンプルをあらためて整理します。抽象メソッドの強制力や共通メソッドの便利さを意識しながら読むと理解が深まります。
// 抽象クラス
abstract class Vehicle {
abstract start(): void;
move(): void {
console.log("のりものが進みます");
}
}
// 子クラスの実装
class Car extends Vehicle {
start(): void {
console.log("エンジンをかけました");
}
}
class Bike extends Vehicle {
start(): void {
console.log("ペダルをこぎ始めました");
}
}
const car = new Car();
car.start();
car.move();
const bike = new Bike();
bike.start();
bike.move();
このサンプルを見ると、どちらの子クラスでも同じメソッド名で動作を実装しているため、車と自転車のどちらを使う場合でも統一された操作で扱えることが理解できます。これが抽象クラスを利用する大きな利点であり「共通ルールを持つクラス設計」がもたらす効果です。
生徒
「抽象クラスって最初は漠然としていたんですが、実際に使ってみると全体の見通しが良くなる仕組みなんですね。」
先生
「そうですね。特に複数のクラスが似たような構造をもつ場合にはとても役立ちます。共通の設計図を用意しておくことで、それぞれのクラスに共通する部分をまとめて管理できます。」
生徒
「抽象メソッドを必ず実装しないといけない点も、作り忘れを防ぐ効果があると分かりました。」
先生
「そのとおりです。ルールが明確だと自然とコードも整理されていきますし、他の人が見ても理解しやすい構造になります。」
生徒
「抽象クラスをもっと使って設計を練習してみたくなりました!」
先生
「良い心がけですね。実際の開発でも抽象クラスが役立つ場面は多いので、ぜひ積極的に活用してみてください。」