継承


継承の基礎

考え方

継承とは、あるクラスに派生する別のクラスを生成する際に使用する。
例えば、既に「ヒーロー」クラスがあり、それに派生する「スーパーヒーロー」クラスを生成する際に使用する。

使用方法

class クラス名 extends 元にするクラス名{
元のクラス名に追加するメンバ
}

(Heroクラス)

public class Hero {
    //フィールド
    private String name;
    private int hp;
}

これに空を飛ぶスーパーヒーローを追加したい場合・・・

(SuperHeroクラス)

public class SuperHero extends Hero{
    private boolean flying;

    public void fly() {
        this.flying = true;
        System.out.println("飛び上がった!");
    }

    public void land() {
        this.flying = false;
        System.out.println("着地した!");
    }
}

継承関係の表現方法

「元のクラス」・・・親クラス、スーパークラス
「元のクラスを元に作成したクラス」・・・子クラス、サブクラス

上記のコードでいうと、継承関係は以下の通り。
「Heroクラス」・・・親クラス、スーパークラス
「SuperHeroクラス」・・・子クラス、サブクラス

継承の禁止事項

・継承時に複数の親クラスを継承(多重継承)出来ない。
・クラス作成時にfinalが付いたクラスは、継承不可。

オーバーライド

考え方

親クラスのメンバを、子クラスで書き換えること。この時親クラスのメンバは書き変わらない。

runメソッドを書き換えたい場合。
(親クラス)

public class Hero {
    //フィールド
    private String name;
    private int hp;

    public void run() {
        System.out.println("逃げ出した!");
    }

(子クラス)

public class SuperHero extends Hero{
    private boolean flying;

    public void run() {//書き変えるメンバ
        System.out.println("撤退した!");
    }
}

(メインクラス)


public static void main(String[] args) {
        //ヒーロー
        Hero hero1 = new Hero();
        hero1.run();

        //スーパーヒーロー
        SuperHero shHero1 = new SuperHero();
        shHero1.run();
    }

(出力結果)

逃げ出した
撤退した

オーバーライドの禁止方法

オーバライド禁止のメソッドにfinalを付ける事で、禁止出来る。

runメソッドのオーバーライドを禁止したい場合。
public final void run

継承時のクラス状態

考え方

上記で作成した、SuperHeroインスタンスは、HeroインスタンスSuperHeroインスタンスの2重構造になっている。

(イメージ図)

呼び出し時の動作

呼び出し時は、まずSuperHeroインスタンスのメンバから利用するよう動作する。

(イメージ図)

この場合runメソッドは'SuperHero`内にあるため、そちらが呼び出される。
そして、①でメソッドが見つかったため、②の呼び出しは実行されない。

親クラスへのアクセス方法

考え方

親クラスのメンバを子インスタンスで使用する方法

使用方法

フィールド・・・super.フィールド名
メソッド・・・super.メソッド名(引数)

SuperHeroクラスの、attackメソッドでは、飛んでいる場合2回攻撃する。
攻撃の中身は親クラスのHeroクラスattackメソッドを使用したい場合。

//Heroクラス
    public void attack(Matango matango) {
        int matangoHp = matango.getHp() - 5;
        matango.setHp(matangoHp);
    }
//SuperHeroクラス
    public void attack(Matango matango) {
        super.attack(matango);
        if(this.flying) {
            super.attack(matango);
        }

注意点

親クラスの親クラス(祖父母クラス)へのアクセスは不可。

継承とコンストラクタについて

考え方

継承によって生成されているインスタンスは、自身のコンストラクタ呼び出し前に必ず親コンストラクタも呼び出す。

親クラスのコンストラクタの呼び出し方法

子クラス名(){
    super(引数);
}

しかし、何も記載しない場合、暗黙のsuperが自動で入る。

(Heroクラス)

Hero(){
    System.out.println("Heroを生成しました");
}

(SuperHeroクラス)

SuperHero(){
                //ここに暗黙のsuperが入る
    System.out.println("SuperHeroを生成しました");
}

(mainクラス)

SuperHero shHero1 = new SuperHero();

(出力結果)
SuperHeroは生成してないが、SuperHeroのコンストラクタが呼び出される。

Heroを生成しました
SuperHeroを生成しました

補足

親クラスのコンストラクタに引数が必要な場合は、super(引数)の引数に明示的に値を入れる必要がある。

継承の際の注意点

is-aの関係

子クラスis-a親クラス(子クラスは親クラスの一種である)が成り立たないとだめ。

○正しい例: 子クラス(TV)ー親クラス(家電製品)「TVは家電製品である」→成り立つ
×誤った例: 子クラス(TV)ー親クラス(自動車)「TVは自動車である」→成り立たない