Java超|Javaの継承の基本

親クラスの力を受け継ぎ、子クラスで自分だけの技を加える。Javaの継承は、共通部分をまとめて、クラスを強く育てるための仕組みです。

Javaのオブジェクト指向では、すでに作ったクラスを土台にして、新しいクラスを作ることができます。

この仕組みを継承といいます。

継承を使うと、似たような性質を持つクラスを毎回最初から作らなくてよくなります。共通している情報や処理は親クラスにまとめておき、子クラスでは必要な特徴だけを追加できます。

ドラゴンボールの世界観でたとえると、まず SaiyanWarrior というサイヤ人戦士の共通の土台があります。

サイヤ人戦士であれば、名前を持ち、戦闘力を持ち、状態を表示できます。

そこから、さらに特別な戦士である SuperSaiyanWarrior を作る場合、サイヤ人戦士としての基本はそのまま受け継ぎます。そのうえで、変身段階や必殺技の強化倍率のような追加要素を持たせるイメージです。

つまり継承は、共通部分を親クラスにまとめ、個別の特徴を子クラスに追加する考え方です。コードの重複を減らし、クラスを拡張しやすくするための大切な仕組みです。この記事では、継承、スーパークラス、サブクラス、extends、クラスの拡張について、ドラゴンボールの世界観で解説します。

継承とは何か

継承とは、すでにあるクラスのフィールドやメソッドを受け継いで、新しいクラスを作る仕組みです。

ドラゴンボール風にたとえると、まず SaiyanWarrior というサイヤ人戦士の共通クラスがあるとします。

このクラスには、サイヤ人戦士なら多くのキャラクターが持っている基本情報をまとめます。

項目内容
名前戦士の名前
戦闘力戦士としての基本的な強さ
行動状態を表示する、戦闘力を設定する

この共通の土台をもとにして、さらに特別な戦士を表すクラスを作れます。

たとえば、スーパーサイヤ人のような進化した戦士を表すなら、次のように考えられます。

クラス名役割
SaiyanWarriorサイヤ人戦士の共通の土台
SuperSaiyanWarriorサイヤ人戦士を受け継いだ特別な戦士

このとき、SuperSaiyanWarrior は SaiyanWarrior の機能を受け継ぎます。

さらに、SuperSaiyanWarrior だけが持つ情報や機能を追加できます。

つまり継承とは、親クラスの基本能力を受け継ぎながら、子クラスで新しい特徴を加える仕組みです。

クラスを拡張するとはどういうことか

クラスを拡張するとは、もともとあるクラスを土台にして、新しいフィールドやメソッドを追加したクラスを作ることです。

ドラゴンボール風にたとえると、普通のサイヤ人戦士を土台にして、スーパーサイヤ人としての役割や能力を追加するイメージです。

たとえば、サイヤ人戦士には次のような共通情報があります。

共通情報内容
name名前
battlePower戦闘力
setWarrior()名前と戦闘力を設定する
show()状態を表示する

ここから、スーパーサイヤ人クラスには次のような追加情報を持たせます。

追加情報内容
transformationLevel変身段階
setTransformationLevel()変身段階を設定する

このとき大切なのは、親クラスにある name、battlePower、setWarrior()、show() を子クラスにもう一度書かなくてもよいことです。

子クラスは親クラスを受け継いでいるため、親クラスの機能を使えます。

これがクラスを拡張する大きなメリットです。

図:親クラスから子クラスへ受け継ぐイメージ

この図が示していること

この図では、上にある SaiyanWarrior クラスが親クラスです。

そこから下の SuperSaiyanWarrior クラスへ矢印が伸びています。

この矢印は、SaiyanWarrior のメンバが SuperSaiyanWarrior に受け継がれていることを表します。

SuperSaiyanWarrior の中には、親から受け継いだ name、battlePower、setWarrior()、show() があります。

さらに、SuperSaiyanWarrior 独自の transformationLevel、setTransformationLevel() も追加されています。

つまり、サブクラスは、親クラスの共通機能と、子クラス独自の特徴を両方持てるということです。

スーパークラスとサブクラス

継承では、親と子の関係を表すために、次の用語を使います。

用語意味ドラゴンボール風にたとえると
スーパークラス継承元になる親クラスサイヤ人戦士という共通の土台
サブクラス親を受け継いで作る子クラススーパーサイヤ人のように特別な力を持つ戦士

今回の例では、次の関係になります。

クラス名役割
SaiyanWarriorスーパークラス
SuperSaiyanWarriorサブクラス

ここで大切なのは、サブクラスはスーパークラスとまったく別の存在ではないという点です。

SuperSaiyanWarrior は、SuperSaiyanWarrior としての独自要素を持ちながら、SaiyanWarrior としての性質も持っています。

ドラゴンボール風にたとえると、スーパーサイヤ人は特別な戦士ですが、まずサイヤ人戦士でもあるという関係です。

このように、子クラスは親クラスの一種として考えると、継承の関係が理解しやすくなります。

extends の意味

Javaでクラスを継承するときは、extends を使います。

書き方は次の形です。

class サブクラス名 extends スーパークラス名
{
    サブクラスに追加するメンバ
}

たとえば、SuperSaiyanWarrior が SaiyanWarrior を継承する場合は、次のように書きます。

class SuperSaiyanWarrior extends SaiyanWarrior
{
    // スーパーサイヤ人だけの追加要素
}

この extends は、このクラスは指定した親クラスを受け継いで作ります、という意味です。

とても短い記述ですが、意味は大きいです。

この1行によって、SuperSaiyanWarrior は SaiyanWarrior にあるメソッドを使えるようになります。

クラスの継承を確認する

ファイル名:Sample1.java

class SaiyanWarrior
{
    private String name;
    private int battlePower;

    public SaiyanWarrior()
    {
        name = "戦士未登録";
        battlePower = 0;
        System.out.println("サイヤ人戦士クラスの戦士を作成しました。");
    }

    public void setWarrior(String n, int b)
    {
        name = n;
        battlePower = b;
        System.out.println("名前を" + name + "に、戦闘力を" + battlePower + "にしました。");
    }

    public void show()
    {
        System.out.println("戦士の名前は" + name + "です。");
        System.out.println("戦闘力は" + battlePower + "です。");
    }
}

class SuperSaiyanWarrior extends SaiyanWarrior
{
    private int transformationLevel;

    public SuperSaiyanWarrior()
    {
        transformationLevel = 0;
        System.out.println("スーパーサイヤ人クラスの戦士を作成しました。");
    }

    public void setTransformationLevel(int level)
    {
        transformationLevel = level;
        System.out.println("変身段階を" + transformationLevel + "にしました。");
    }
}

class Sample1
{
    public static void main(String[] args)
    {
        SuperSaiyanWarrior warrior1 = new SuperSaiyanWarrior();

        warrior1.setWarrior("悟空", 9000);
        warrior1.setTransformationLevel(1);

        warrior1.show();
    }
}

このプログラムでは、SaiyanWarrior がスーパークラス、SuperSaiyanWarrior がサブクラスです。

それぞれの役割を整理すると、次のようになります。

クラス持っているもの
SaiyanWarrior名前、戦闘力、名前と戦闘力を設定する機能、状態表示機能
SuperSaiyanWarrior変身段階、変身段階を設定する機能

ここで注目したいのは、SuperSaiyanWarrior クラスの中に name、battlePower、setWarrior()、show() を書いていないことです。

それでも、main メソッドでは次のように使えています。

warrior1.setWarrior("悟空", 9000);
warrior1.show();

これは、SuperSaiyanWarrior が SaiyanWarrior を継承しているからです。

つまり、SuperSaiyanWarrior のオブジェクトは、自分自身が持つ setTransformationLevel() だけでなく、親クラスから受け継いだ setWarrior() や show() も使えます。

スーパークラス SaiyanWarrior の役割

まず、親クラスである SaiyanWarrior を見てみましょう。

class SaiyanWarrior
{
    private String name;
    private int battlePower;

ここでは、サイヤ人戦士に共通する情報として name と battlePower を持たせています。

ドラゴンボール風にたとえると、どのサイヤ人戦士にも名前と戦闘力があります。

これはスーパーサイヤ人だけでなく、普通のサイヤ人戦士にも共通する情報です。

だから、SaiyanWarrior にまとめるのが自然です。

SaiyanWarrior には、次のメソッドもあります。

メソッド役割
SaiyanWarrior()戦士を初期状態で作る
setWarrior(String n, int b)名前と戦闘力を設定する
show()戦士の状態を表示する

これらは、特別な戦士だけの機能ではありません。

サイヤ人戦士として共通して使えそうな機能です。

継承を使うときは、まず次のように考えると分かりやすいです。

見るポイント考え方
全員に共通するか共通するなら親クラスへ
特定の種類だけが持つか特定のものなら子クラスへ

SaiyanWarrior は、サイヤ人戦士全体の共通部分をまとめる役割を持っています。

サブクラス SuperSaiyanWarrior の役割

次に、子クラスである SuperSaiyanWarrior を見てみましょう。

class SuperSaiyanWarrior extends SaiyanWarrior
{
    private int transformationLevel;

ここでは、extends SaiyanWarrior と書いています。

これにより、SuperSaiyanWarrior は SaiyanWarrior を継承します。

SuperSaiyanWarrior では、親クラスから受け継いだ機能に加えて、transformationLevel を追加しています。

この transformationLevel は、スーパーサイヤ人としての変身段階のようなものです。

ドラゴンボール風にたとえると、スーパーサイヤ人はただのサイヤ人戦士ではなく、変身によって強化された特別な状態です。

そのため、変身段階という追加情報を持たせています。

さらに、setTransformationLevel() で変身段階を設定します。

public void setTransformationLevel(int level)
{
    transformationLevel = level;
    System.out.println("変身段階を" + transformationLevel + "にしました。");
}

この setTransformationLevel() は SuperSaiyanWarrior 独自のメソッドです。

SaiyanWarrior にはありません。

つまり SuperSaiyanWarrior は、親の機能と子の機能を組み合わせています。

受け継いだ機能独自に追加した機能
setWarrior()setTransformationLevel()
show()transformationLevel の管理
name と battlePower の管理変身段階の管理

サブクラスのコンストラクタ

SuperSaiyanWarrior のコンストラクタは次の部分です。

public SuperSaiyanWarrior()
{
    transformationLevel = 0;
    System.out.println("スーパーサイヤ人クラスの戦士を作成しました。");
}

ここでは、SuperSaiyanWarrior 独自のフィールドである transformationLevel を初期化しています。

ドラゴンボール風にたとえると、スーパーサイヤ人として戦士を登録したときに、変身段階をいったん 0 にしておくようなイメージです。

ここで見ておきたいのは、サブクラスのコンストラクタでは、子クラスで追加した要素を中心に初期化していることです。

親クラスにある name や battlePower は SaiyanWarrior 側で管理しています。

子クラスである SuperSaiyanWarrior は、自分に追加された transformationLevel を管理します。

このように、親と子で役割を分けると、コードの見通しがよくなります。

main メソッドの流れ

main メソッドでは、次の流れで処理が進みます。

SuperSaiyanWarrior warrior1 = new SuperSaiyanWarrior();

ここでは、SuperSaiyanWarrior 型のオブジェクトを作っています。

warrior1 は、スーパーサイヤ人クラスの戦士として生成されます。

次に、名前と戦闘力を設定しています。

warrior1.setWarrior("悟空", 9000);

この setWarrior() は SuperSaiyanWarrior には書かれていません。

しかし、親クラス SaiyanWarrior にあるため、SuperSaiyanWarrior のオブジェクトから呼び出せます。

次に、変身段階を設定しています。

warrior1.setTransformationLevel(1);

この setTransformationLevel() は SuperSaiyanWarrior に追加された独自メソッドです。

最後に、状態を表示しています。

warrior1.show();

show() も SaiyanWarrior から受け継いだメソッドです。

処理の流れを表にすると、次のようになります。

呼び出し定義されている場所意味
new SuperSaiyanWarrior()SuperSaiyanWarriorスーパーサイヤ人クラスの戦士を作る
setWarrior("悟空", 9000)SaiyanWarrior名前と戦闘力を設定する
setTransformationLevel(1)SuperSaiyanWarrior変身段階を設定する
show()SaiyanWarrior戦士の状態を表示する

この表を見ると、1つのオブジェクトが親クラスと子クラスの両方の機能を使っていることが分かります。

これが継承の大きな特徴です。

実行結果の例

サイヤ人戦士クラスの戦士を作成しました。
スーパーサイヤ人クラスの戦士を作成しました。
名前を悟空に、戦闘力を9000にしました。
変身段階を1にしました。
戦士の名前は悟空です。
戦闘力は9000です。

実行結果では、まずサイヤ人戦士クラスのメッセージが表示され、そのあとスーパーサイヤ人クラスのメッセージが表示されます。

これは、SuperSaiyanWarrior のオブジェクトを作るとき、親クラスである SaiyanWarrior の部分も関係しているためです。

そのあと、名前と戦闘力を設定し、変身段階を設定しています。

最後に show() によって、戦士の名前と戦闘力が表示されます。

図:オブジェクトが親と子の機能を使うイメージ

この図が示していること

この図では、warrior1 という SuperSaiyanWarrior オブジェクトの中に、親から受け継いだ機能と、自分で追加した機能が一緒に存在している様子を表しています。

親クラス SaiyanWarrior からは、setWarrior() や show() を受け継いでいます。

子クラス SuperSaiyanWarrior では、setTransformationLevel() や transformationLevel を追加しています。

つまり、サブクラスのオブジェクトは、親から受け継いだものと、子で追加したものを両方使えるということです。

継承をドラゴンボール風に感覚で理解する

サイヤ人戦士という共通の型があるとします。

その型には、次のような基本があります。

共通の基本内容
名前誰なのか
戦闘力どれくらいの力を持つのか
状態表示自分の情報を示す

そこから、スーパーサイヤ人という特別な型を作るとします。

スーパーサイヤ人は、サイヤ人戦士としての基本を持ちながら、さらに変身段階のような情報を持ちます。

つまり、サイヤ人戦士としての共通部分を受け継ぎ、スーパーサイヤ人としての特徴を追加するということです。

これが継承の感覚です。

Javaのコードでは、この関係を extends で表します。

class SuperSaiyanWarrior extends SaiyanWarrior

この1行によって、SuperSaiyanWarrior は SaiyanWarrior の性質を受け継いだクラスになります。

継承で大切な親子関係の考え方

継承は便利ですが、何でもかんでも使えばよいわけではありません。

大切なのは、親子関係が自然かどうかです。

継承を使うときは、次のように考えると分かりやすいです。

判断ポイント
子クラスは親クラスの一種と言えるかSuperSaiyanWarrior は SaiyanWarrior の一種
共通部分を親にまとめられるか名前、戦闘力、状態表示
子クラスだけの追加要素があるか変身段階、変身段階設定

今回の例では、SuperSaiyanWarrior は SaiyanWarrior の一種です。

そのため、継承として自然です。

ドラゴンボール風にたとえると、次のように考えられます。

関係継承として自然か
スーパーサイヤ人はサイヤ人戦士の一種自然
サイヤ人戦士はスカウターの一種不自然
超サイヤ人ゴッド系戦士はスーパーサイヤ人系戦士の一種自然

このように、子クラスは親クラスの一種である、という関係で考えると、継承を使うべき場面が見えてきます。

private のフィールドはどう考えるか

Sample1.java では、SaiyanWarrior の name と battlePower に private を付けています。

private String name;
private int battlePower;

private が付いているフィールドは、そのクラスの外から直接触れません。

つまり、SuperSaiyanWarrior は SaiyanWarrior を継承していますが、name や battlePower を直接操作するのではなく、setWarrior() や show() を通して扱います。

これは、ドラゴンボール風にたとえると、戦士管理本部の重要情報を勝手に書き換えられないようにしているイメージです。

書き方意味
private String name名前を外部から直接変更させない
private int battlePower戦闘力を外部から直接変更させない
setWarrior()決められた方法で名前と戦闘力を設定する
show()決められた方法で状態を表示する

継承しているからといって、親クラスの private フィールドを子クラスが自由に直接触れるわけではありません。

この点は、継承を学ぶときに大切です。

親クラスと子クラスの役割分担

継承では、親クラスと子クラスの役割分担を意識すると理解しやすくなります。

今回の例では、次のように分かれています。

担当内容
SaiyanWarriorサイヤ人戦士としての共通情報を持つ
SuperSaiyanWarriorスーパーサイヤ人としての追加情報を持つ
main メソッドオブジェクトを作って、親と子の機能を使う

親クラスには、共通して使うものを置きます。

子クラスには、その子クラスだけが必要とするものを置きます。

この切り分けができると、クラス設計がかなり分かりやすくなります。

図:共通部分と追加部分を分ける継承設計

この図が示していること

この図では、継承を使って共通部分と追加部分を分ける考え方を表しています。

SaiyanWarrior には、name、battlePower、setWarrior()、show() のような共通部分をまとめています。

SuperSaiyanWarrior には、transformationLevel、setTransformationLevel() のような追加部分を置いています。

extends によって、SuperSaiyanWarrior は SaiyanWarrior の共通機能を受け継ぎます。

ここから分かるのは、継承は単にクラスをつなげる文法ではなく、共通部分を親にまとめ、子で個性を追加するための設計の考え方だということです。

継承は設計の再利用である

継承をひとことで表すなら、設計の再利用です。

一度作った親クラスを活かしながら、新しいクラスを作れます。

これにより、共通部分を繰り返し書かずに済みます。

ドラゴンボール風にたとえると、サイヤ人戦士としての共通ルールを最初に作っておきます。

そのうえで、スーパーサイヤ人、超サイヤ人ゴッド系戦士、惑星戦士流の精鋭戦士のように、必要な特徴を追加していくイメージです。

Javaで継承を学ぶときは、文法だけを見るのではなく、次の視点を持つと理解しやすくなります。

視点考えること
共通部分どのクラスにも必要なものは何か
追加部分子クラスだけに必要なものは何か
親子関係子クラスは親クラスの一種と言えるか
再利用親クラスの機能を使い回せるか

継承は、クラスを強くするための仕組みです。

親クラスに共通の力をまとめ、子クラスで個性を加えることで、コードは読みやすく、拡張しやすくなります。