
Java道|12章のまとめ
抽象クラスで共通の土台を整え、インターフェイスで能力の約束を結ぶ。
12章は、Javaのオブジェクト指向を「クラスを作る学習」から「大きな設計を組み立てる学習」へ進める章です。
12章では、Javaのオブジェクト指向をさらに一歩進めるために、抽象クラス と インターフェイス を学びました。
11章で継承の基本を学ぶと、親クラスから子クラスへ機能を受け継ぐ考え方が見えてきます。
しかし、実際のプログラム設計では、それだけでは足りない場面があります。
たとえば、鬼滅の刃風にたとえると、和風剣士たちはそれぞれ違う個性を持っています。
| 和風剣士の種類 | 個性 |
|---|---|
| WaterSlayer | 水の呼吸を使う剣士 |
| FlameSlayer | 炎の呼吸を使う剣士 |
| SupportSlayer | 仲間を支援する剣士 |
| ScoutSlayer | 索敵を得意とする剣士 |
このように個性は違いますが、全員に共通するものもあります。
| 共通するもの | 内容 |
|---|---|
| name | 剣士の名前 |
| speed | 移動速度 |
| show() | 自分の情報を表示する処理 |
| move() | 移動する能力 |
| useBreath() | 呼吸を使う能力 |
ここで大切になるのが、共通の土台 と 能力の約束 を分けて考えることです。
抽象クラスは、まだ完成していないけれど、共通部分を持った土台を作るための仕組みです。
インターフェイスは、クラスの種類に関係なく「この能力を持つ」という約束を表す仕組みです。
12章では、抽象クラスとインターフェイスを使って、クラスを整理し、増やしやすくし、共通のルールで扱うための考え方を学びました。
抽象クラスは未完成の土台を作るための仕組み
抽象クラスは、普通のクラスのようにフィールドやメソッドを持てます。
しかし、普通のクラスと大きく違う点があります。
それは、抽象クラスそのものからはオブジェクトを作成できない という点です。
抽象クラスは完成品ではなく、サブクラスのための共通の土台です。
鬼滅の刃風にたとえると、抽象クラス Slayer は、「和風剣士なら最低限こういうものを持つ」という設計図です。
| Slayer に置くもの | 役割 |
|---|---|
| name | 剣士の名前 |
| speed | 移動速度 |
| setSpeed() | 速度を設定する共通処理 |
| showBaseInfo() | 名前や速度を表示する共通処理 |
| show() | 子クラスで完成させる情報表示 |
剣士なら名前や速度を持つ、という点は共通です。
しかし、どんな情報を表示するかは剣士ごとに違います。
WaterSlayer なら、水の呼吸の型を表示したいかもしれません。
FlameSlayer なら、炎の技を表示したいかもしれません。
SupportSlayer なら、治療や補助の能力を表示したいかもしれません。
そこで抽象クラスでは、共通部分だけを用意し、クラスごとに違う部分はサブクラスに任せます。
抽象メソッドは必ず持つべき処理を宣言する
抽象クラスの中では、抽象メソッド を宣言できます。
抽象メソッドとは、処理内容を書かずに、メソッドの形だけを決めるメソッドです。
public abstract void show();これは、
剣士なら show() を必ず持つ。ただし、表示内容は各サブクラスで決める
という意味です。
鬼滅の刃風にたとえると、隊の規則として「剣士は任務前に自分の情報を示せ」と決めているようなものです。
ただし、水の剣士と炎の剣士と支援剣士では、名乗る内容が違います。
| クラス | show() で表示する内容 |
|---|---|
| WaterSlayer | 名前、速度、水の呼吸 |
| FlameSlayer | 名前、速度、炎の技 |
| SupportSlayer | 名前、速度、支援技術 |
親クラスである Slayer は、show() が必要であることだけを決めます。
実際の中身は、それぞれの子クラスが完成させます。
これにより、親クラスは共通ルールを示し、子クラスは自分らしい処理を作れるようになります。
抽象クラスのオブジェクトを作成できない理由
抽象クラスは、直接 new でオブジェクトを作成できません。
// これはできない
Slayer slayer = new Slayer();理由はシンプルです。
抽象クラスは未完成だからです。
鬼滅の刃風にたとえると、Slayer は「剣士」という共通概念です。
しかし、「剣士」という概念だけでは、実際に任務へ出る人物にはなりません。
実際に動けるのは、次のような具体的なクラスです。
| 抽象的な土台 | 具体的なクラス |
|---|---|
| Slayer | WaterSlayer |
| Slayer | FlameSlayer |
| Slayer | SupportSlayer |
抽象クラスは、半端なクラスではありません。
あえて未完成にしておくことで、共通部分を整理し、具体的な部分をサブクラスに任せるための設計道具です。
インターフェイスは何ができるかの約束を表す
12章でもうひとつ大切なテーマが、インターフェイス です。
インターフェイスは、クラスと少し似ていますが、役割は違います。
抽象クラスは、共通の土台を表します。
インターフェイスは、何ができるか という能力の約束を表します。
鬼滅の刃風に考えると、和風剣士にはいろいろな能力があります。
| 能力 | インターフェイスの例 | 意味 |
|---|---|---|
| 移動できる | iMovable | move() を持つ |
| 呼吸を使える | iBreathUser | useBreath() を持つ |
| 情報を表示できる | iDisplayable | show() を持つ |
| 支援できる | iSupportable | support() を持つ |
ここで大切なのは、これらは「何者か」ではなく「何ができるか」を表している点です。
WaterSlayer は水の剣士です。
SupportSlayer は支援剣士です。
この2つは、クラスとしては違う存在です。
しかし、どちらも移動できるなら、iMovable を実装できます。
どちらも情報を表示できるなら、iDisplayable を実装できます。
| クラス | 何者か | 何ができるか |
|---|---|---|
| WaterSlayer | 水の剣士 | 移動できる、呼吸を使える、表示できる |
| FlameSlayer | 炎の剣士 | 移動できる、呼吸を使える、表示できる |
| SupportSlayer | 支援剣士 | 移動できる、支援できる、表示できる |
このように、インターフェイスを使うと、クラスの種類をまたいで共通の能力を整理できます。
インターフェイスのフィールドは定数になる
インターフェイスにフィールドを書くと、それは基本的に定数として扱われます。
インターフェイスは、個々のオブジェクトの状態を管理する場所ではありません。
能力や約束に関する共通ルールを置く場所です。
鬼滅の刃風にたとえると、インターフェイスに書くフィールドは、個々の剣士の現在の体力や速度ではありません。
それよりも、隊全体で共有する固定ルールのようなものです。
| 書く場所 | 向いている内容 |
|---|---|
| クラスのフィールド | 剣士ごとの名前、速度、状態 |
| インターフェイスのフィールド | 固定された共通ルール、定数 |
たとえば、次のような値はインターフェイスに置く意味があります。
interface iMissionRule
{
int MAX_MISSION_LEVEL = 10;
}この MAX_MISSION_LEVEL は、変化する状態ではなく、固定された共通ルールとして扱います。
インターフェイスは、状態を管理するためのものではなく、能力やルールを表すためのものだと考えると整理しやすいです。
インターフェイスのメソッドは抽象メソッドになる
インターフェイスに書くメソッドは、基本的に抽象メソッドです。
つまり、処理の中身は書かずに、メソッドの形だけを決めます。
interface iMovable
{
void move();
}これは、
iMovable を実装するクラスは、move() を必ず持つ
という約束です。
実際の移動のしかたは、クラスごとに違ってかまいません。
| クラス | move() の中身 |
|---|---|
| WaterSlayer | 水の流れのように移動する |
| FlameSlayer | 炎の勢いで前線へ踏み込む |
| SupportSlayer | 仲間の近くへ素早く移動する |
インターフェイスは、メソッド名を並べるだけのものではありません。
クラスに共通の行動ルールを守らせるための仕組みです。
インターフェイスを実装すると多くのクラスをまとめて扱える
インターフェイスを使う大きな利点は、違うクラスを同じ型としてまとめて扱えることです。
たとえば、WaterSlayer、FlameSlayer、SupportSlayer がすべて iMovable を実装しているとします。
すると、次のように iMovable 型の配列でまとめられます。
iMovable[] members = new iMovable[3];この配列には、iMovable を実装したクラスのオブジェクトを入れられます。
| 配列要素 | 実体 | 共通して呼び出せるメソッド |
|---|---|---|
| members[0] | WaterSlayer | move() |
| members[1] | FlameSlayer | move() |
| members[2] | SupportSlayer | move() |
呼び出し側は、それぞれの具体的なクラス名を細かく気にしなくても、move() を呼び出せます。
for(int i = 0; i < members.length; i++){
members[i].move();
}同じ move() という呼び出しでも、実際には各クラスに合った動きが実行されます。
これが、インターフェイスと多態性の大きな力です。
スーパーインターフェイスを拡張してサブインターフェイスを作れる
インターフェイス同士にも親子関係を作れます。
インターフェイスが別のインターフェイスを受け継ぐときは、extends を使います。
interface iBattleSlayer extends iMovable
{
void attack();
}この場合、iBattleSlayer は iMovable の約束を受け継ぎます。
さらに、自分自身の約束として attack() を追加しています。
| インターフェイス | 持っている約束 |
|---|---|
| iMovable | move() |
| iBattleSlayer | move()、attack() |
拡張される側をスーパーインターフェイス、拡張する側をサブインターフェイスと考えると分かりやすいです。
| 用語 | 意味 | 鬼滅の刃風のイメージ |
|---|---|---|
| スーパーインターフェイス | 基本となる能力の約束 | 移動できる |
| サブインターフェイス | 基本能力を受け継ぎ、追加能力を持つ約束 | 移動できて戦える |
鬼滅の刃風にたとえると、まず「移動できる」という基本の能力があります。
その上に、「移動できるうえに攻撃もできる」という発展能力を作るイメージです。
この仕組みによって、能力の約束を段階的に整理できます。
抽象クラスとインターフェイスを使うと多態性で分かりやすいコードが書ける
12章全体を通して大切なのは、抽象クラスとインターフェイスを使うことで、多態性をより分かりやすく使えるようになる点です。
鬼滅の刃風に整理すると、次のようになります。
| 役割 | しくみ | 例 |
|---|---|---|
| 剣士としての共通の土台 | 抽象クラス | Slayer |
| 移動できる能力の約束 | インターフェイス | iMovable |
| 呼吸を使える能力の約束 | インターフェイス | iBreathUser |
| 実際の剣士 | 具体クラス | WaterSlayer、FlameSlayer、SupportSlayer |
このように分けると、コードを書く側は、共通の型でまとめて扱いやすくなります。
Slayer 型なら、剣士としてまとめられます。
iMovable 型なら、移動できる者としてまとめられます。
iBreathUser 型なら、呼吸を使える者としてまとめられます。
| まとめ方 | 型 | 見方 |
|---|---|---|
| 剣士としてまとめる | Slayer[] | 共通の土台で見る |
| 移動できる者としてまとめる | iMovable[] | 能力で見る |
| 呼吸を使える者としてまとめる | iBreathUser[] | 能力で見る |
同じ命令でも、実際の処理はオブジェクトごとに変わります。
これが、共通のルールでまとめつつ、具体的な動きはクラスごとに変えられる というオブジェクト指向の強さです。
大規模なプログラムでは抽象クラスとインターフェイスが特に重要になる
小さなプログラムでは、個別のクラスをそのまま使っても何とかなることがあります。
しかし、クラスが増えると、次のような設計が重要になります。
| 考えること | 理由 |
|---|---|
| 共通の土台をどこに置くか | 重複を減らすため |
| 共通の能力をどう表すか | まとめて扱いやすくするため |
| 新しいクラスをどう追加するか | 拡張しやすくするため |
| どの型でまとめるか | 多態性を活かすため |
抽象クラスを使えば、共通の状態や処理を整理できます。
インターフェイスを使えば、能力の約束を整理できます。
多態性を使えば、具体的なクラス名を細かく意識しすぎず、共通ルールで扱えます。
鬼滅の刃風にたとえると、隊士が少ないうちは、1人ずつ個別に管理してもよいかもしれません。
しかし、隊士が増え、柱、支援剣士、索敵役、治療役などが増えてくると、共通の土台や能力の約束で整理しないと、全体が見えにくくなります。
12章で学んだ抽象クラスとインターフェイスは、大きなプログラムを整理するための大切な設計道具です。
12章で学んだ内容を表で整理
| 学んだこと | 意味 |
|---|---|
| 抽象クラスを宣言できる | 未完成の共通土台を作れる |
| 抽象クラスは抽象メソッドを持てる | 子クラスに必ず実装させたい処理を宣言できる |
| 抽象クラスのオブジェクトは作れない | そのままでは未完成だから |
| インターフェイスを宣言して実装できる | 能力や約束をクラスに持たせられる |
| インターフェイスのフィールドは定数になる | 固定された共通ルールを表す |
| インターフェイスのメソッドは抽象メソッドになる | 処理内容は実装クラスが決める |
| サブインターフェイスを宣言できる | 約束を段階的に拡張できる |
| 多態性でまとめて扱える | 共通の型で呼び出し、実体ごとの処理を動かせる |
図:共通の型でまとめて扱う多態性
↓クリックすると拡大表示されます。

この図が示していること
この図では、12章で学んだ多態性の使い方を整理しています。
左側の Slayer[] slayers では、WaterSlayer、FlameSlayer、SupportSlayer を剣士としてまとめています。
この場合、共通して show() を呼び出せます。
右側の iMovable[] movableMembers では、移動できるクラスをまとめています。
この場合、共通して move() を呼び出せます。
| まとめ方 | 型 | 呼び出せるメソッド | 見方 |
|---|---|---|---|
| 剣士としてまとめる | Slayer[] | show() | 共通の土台で見る |
| 移動できる者としてまとめる | iMovable[] | move() | 共通の能力で見る |
同じ show() や move() を呼び出しても、実際に動く処理はオブジェクトの実体によって変わります。
この図から分かることは、抽象クラスとインターフェイスを使うと、クラスの違いを保ったまま、共通の型で分かりやすくまとめられるということです。
12章でいちばん大事な感覚
12章で学んだことをひとつの感覚にまとめるなら、
共通の土台と共通の約束を使って、多くのクラスを整理しながら扱う考え方
です。
鬼滅の刃風に整理すると、次のようになります。
| 設計の考え方 | 鬼滅の刃風のイメージ |
|---|---|
| 抽象クラスで共通の土台を作る | 剣士としての名前や速度をまとめる |
| 抽象メソッドで必須処理を決める | 剣士なら情報を表示できるようにする |
| インターフェイスで能力の約束を作る | 移動できる、呼吸を使える、支援できる |
| サブインターフェイスで約束を発展させる | 移動できる者から、戦える者へ能力を広げる |
| 具体クラスで個性を表す | 水の剣士、炎の剣士、支援剣士を作る |
| 多態性でまとめて扱う | 同じ命令で、それぞれに合った動きをさせる |
この感覚がつかめると、Javaのオブジェクト指向は、単なる継承の仕組みではなく、大きなプログラムを整理するための設計技法として見えてきます。
12章は、抽象クラスとインターフェイスを通して、Javaのクラス設計を大きく育てるための土台を作る章です。
