【Java】「抽象クラス(abstract)」って何?継承とインターフェースのいいとこ取りをしよう!
はじめに
みなさんどうも、おげんです。
前回、前々回と「継承」や「インターフェース」についてお話ししてきました。
「同じコードをまとめる継承」と「能力のルールを決めるインターフェース」。
この2つを使いこなせれば、もう立派なJava使いの仲間入りです。
でも、こんな「ワガママな悩み」が出てくることはありませんか?
「共通の便利なコード(名前の管理とか)は親で書きたい!でも、攻撃の方法(メソッド)だけは子クラスで自由に、かつ強制的に書かせたい……!」
継承とインターフェースの「いいとこ取り」をしたい。
そんなエンジニアの願いを叶えてくれるのが、今回紹介する「抽象クラス(abstract)」です。
今回は、一見地味だけど実は「最強の土台」になる抽象クラスについて、RPGの例えで攻略していきましょう!
- 「普通のクラス」と「抽象クラス」の違いがピンとこない人
- インターフェースと抽象クラス、どっちを使えばいいか迷っている人
- 先輩が作った「土台のコード」をスムーズに理解したい人
- 「 abstract 」という単語を聞いて、難しそう…と腰が引けている人
抽象クラスは「未完成の設計図」
「抽象(abstract)」という言葉を聞くと難しく感じますが、プログラミングの世界では「あえて未完成にしておく」という意味で使われます。
「キャラクター」という生き物はいない?
RPGの世界を想像してみてください。「戦士」や「魔法使い」は実在しますが、「キャラクター」という名前の生き物そのものが歩いていることはありませんよね。
「キャラクター」はあくまで、戦士や魔法使いをまとめるための概念(共通の土台)です。
このように、「共通の項目(名前やHP)は持っているけれど、それ単体では存在しないもの」をJavaで表現するのが抽象クラスです。
抽象クラスの役割:あえて「実体化」させない
抽象クラスにすると、以下のようなルールが生まれます。
- インスタンス化(new)ができない:
new Character()と書くとエラーになります。「未完成なんだから、これだけで作っちゃダメだよ!」とJavaが止めてくれるんです。 - 子クラスへの「宿題」: 「攻撃の方法(attack)は、子クラスで必ず具体的に書きなさい!」という強制力を持たせることができます。
「共通部分は親が世話し、具体的な個性は子に任せる」という、絶妙なバランスを保つための仕組みなんですね。
Javaでの書き方:abstract
抽象クラスを作るには、クラス名とメソッド名の前に abstract と付けるだけです。
① 抽象クラス(未完成の土台)を作る
共通で使いたい「名前(name)」と、子クラスで必ず中身を書いてほしい「攻撃(attack)」を定義してみましょう。
// abstract をつけると抽象クラスになる
abstract class Character {
String name;
// 普通のメソッド:共通の動きを書ける
void introduce() {
System.out.println("私は " + name + " です。");
}
// 抽象メソッド:中身( { } )は書かず、セミコロンで終わる
abstract void attack();
}② 子クラスで「宿題」を完成させる
抽象クラスを継承したクラスは、親が残した abstract メソッドの中身を必ず書かなければなりません。
public class Warrior extends Character {
// 親の「宿題(attack)」を具体的に書く
@Override
void attack() {
System.out.println(name + " の剣攻撃!");
}
}abstractメソッド: 「名前だけ決めて、中身は子クラスに丸投げする」という命令です。- 強制力: 子クラスで
attack()を書き忘れると、コンパイルエラーになります。これにより、「攻撃方法が決まっていないキャラクター」が生まれるのを防いでくれます。
「共通の処理(自己紹介)」は親で一括管理しつつ、「個別の処理(攻撃)」は子に強制させる。これで、バグの少ない綺麗な設計ができるようになります。
インターフェースとの使い分け
「抽象クラス」と「インターフェース」。
どちらも「メソッドの中身を子クラスに任せる」という点は同じですが、実は明確な使い分けの基準があります。
一目でわかる比較表
| 特徴 | 抽象クラス(abstract) | インターフェース(interface) |
| 役割 | 「血縁」の土台(共通の性質) | 「資格」の付与(特定の能力) |
| 共通コード | 書ける(名前の変数など) | 基本書かない(ルールのみ) |
| 複数持てる? | 1つだけ(extends) | 何枚でもOK(implements) |
| イメージ | キャラクターの「骨格」 | 外付けの「スキル」 |
どう選ぶのが正解?
迷ったときは、この基準で考えてみてください。
- 「〇〇は、△△の一種である(is-a関係)」なら抽象クラス
- 例:戦士は「キャラクター」の一種。
- 名前やHPなど、「共通のデータ」を持たせたい時に使いましょう。
- 「種類に関係なく、この能力を持たせたい」ならインターフェース
- 例:魔法使いもドローンも「空を飛べる(Flyable)」。
- 種類(親子関係)を超えて、「動きのルールだけ」を共通化したい時に使いましょう。
実務での黄金パターン
実は、これらは「セットで使う」ことが多いです。
「キャラクター」という大きな抽象クラスを作り、その中で必要なキャラにだけ「空を飛ぶ」というインターフェースを実装する。
このように組み合わせることで、無駄がなく、かつ柔軟なプログラムが出来上がります。
まとめ:オブジェクト指向のパズル、完成!
今回は、共通の土台を作りつつ、子クラスにルールを強制する「抽象クラス(abstract)」について学びました。
これまでに学んだ武器を整理してみましょう。
- カプセル化: 大事なデータを「金庫」に入れて守る。
- 継承: 親から子へ、「共通のコード」を引き継いで楽をする。
- ポリモーフィズム: 相手が誰でも「同じ命令」で動かせるようにする。
- インターフェース: 種類を問わず「共通のスキル(資格)」をセットする。
- 抽象クラス: 「未完成の設計図」で、強力な土台を作る。
これらは別々に存在するのではなく、すべてが連携して一つの大きなプログラムを支えています。
最初は「どれを使えばいいの?」と迷うのが当たり前です。
でも、自分でコードを書いていくうちに、「あ、ここはインターフェースにした方が後で楽だな」とか「これは共通の抽象クラスにまとめられそう」という感覚が必ず芽生えてきます。
オブジェクト指向という強力な武器を手に入れたあなたなら、もうどんな複雑なプログラムも一歩ずつ形にしていけるはずです。
今回も最後まで読んでいただき、ありがとうございました!
