【初心者向け】クラスの継承について

プログラミング初心者プログラミング

前回はクラスのカプセル化について記載してみました。

今回はクラスの継承について書いています。
クラスの基本編が前提知識になるので、確認しておいてください。

こちらではクラスの継承を学んだ後に、実際にjavaのコードを見て、継承について理解していきます。
それでは、クラスの継承について確認していきましょう。

クラスの継承とは?

クラスの継承とは?

クラスの継承は、親のクラスのフィールドやメソッドなどを引き継いだ、子のクラスを作成できる機能です。
親のクラスをスーパークラス、子のクラスをサブクラスと呼ぶこともあります。

実際に引き継ぐときには、extendsキーワードをつけて引き継いだりします。
(言語によって、これも書き方が違うことがありますので、仕様を確認しましょう)

javaの場合は下記のように記載すると、親のクラスを引き継ぐことができます。

class Dolphin extends Animal {
  // ....フィールドやメソッドを定義する....
}

サブクラス(子のクラス)はスーパークラス(親のクラス)のメソッドやフィールドを使用することが可能です。
また、サブクラスにはスーパークラスで定義されているメソッドやフィールドを個別に定義することができます。

スーパークラスを拡張しているイメージですね。
抽象的に言うと、親の能力は子供も使えて、さらに子供は能力を増やせてしまう..みたいな感じです。

引き続きjavaのコードを確認して、継承について詳細を見ていきましょう。

継承を使用したコードを見てみよう

継承を使用したコードを見てみよう

それでは、実際にコードでみていきましょう。
今回もjavaを使用して、継承について確認していきます。

このように動物(Animal)のクラスを作成しましたが、これを継承して使ってみます。

class Animal {
    private String genericName;
    private String name;
    private String voice;

    Animal(String genericName,String name,String voice) {
        this.genericName = genericName;
        this.name = name;
        this.voice = voice;
    }

    public void showVoice() {
        System.out.println("我輩は"+ this.genericName + "である。名前は" + this.name + "。鳴き声は" + this.voice + "!");
    }
}

スーパークラスを継承する

下記は動物クラスを継承したイルカクラスです。

class Dolphin extends Animal {
    Dolphin(String genericName,String name,String voice) {
        super(genericName, name, voice);
    }
}

継承するにはこのようにextendsをつけて、継承元のクラス名を書くだけです。
コンストラクタは継承されないので、上記のようにコンストラクタを定義しています。

superで親のコンストラクタに値を渡して、newでインスタンス化されたときに値をオブジェクトに格納しているイメージです。

実際に、このクラスをインスタンス化して使ってみます。

Dolphin dolphin = new Dolphin("イルカ","イルカさん","キュー");
dolphin.showVoice();

このように、DolphinクラスにはshowVoiceは定義されていませんが、親に定義されているので使うことができます。
結果は「我輩はイルカである。名前はイルカさん。鳴き声はキュー!」と出力されます。

サブクラス(子のクラス)に機能を増やしてみよう

サブクラスにメソッドを追加してみます。

class Dolphin extends Animal {
    Dolphin(String genericName,String name,String voice) {
        super(genericName, name, voice);
    }

    public void cryThree() {
        System.out.println(this.voice + "," + this.voice + "," + this.voice);
    }
}

サブクラスにメソッドを追加してみました。
3回鳴き声を表示するだけの内容です。

追加しましたが、このまま使ってみようとするとエラーになります。
親のAnimalクラスのvoiceフィールドがprivateになっているためです。

privateは、そのクラスでしか使えないようにするアクセス修飾子でした。
protectedを設定すると、サブクラスでも使えるようになります。

class Animal {
    private String genericName;
    private String name;
    protected String voice; // protectedに変更する
// ------ 省略 -----
}

voiceprotectedに修正しました。
こうすることで、サブクラスでも使用することができます。
インスタンス化したら、親のメソッドを呼んだときと同じように、下記のように使えます。

Dolphin dolphin = new Dolphin("イルカ","イルカさん","キュー");
dolphin.cryThree();

結果は「キュー,キュー,キュー」となります。
このように、簡単にサブクラスにもメソッドを追加して使うことができます。

実際に使用する際には、共通する処理は親に書いておいて、サブクラスには個別の処理を書くように実装すると良いです。

オーバーライドを確認しよう

親クラスを継承した後に、親クラスと同じメソッド名を定義して、処理を上書きすることができます。
これをオーバーライドと言います。

AnimalクラスにはshowVoiceメソッドが定義してありました。

class Animal {
    // ----- 省略 -----
    public void showVoice() {
        System.out.println("我輩は"+ this.genericName + "である。名前は" + this.name + "。鳴き声は" + this.voice + "!");
    }
}

これをオーバーライドして、Dolphinクラスでは下記のようにしてみます。
出力する内容を変えてみました。

class Dolphin {
    // ----- 省略 -----
    public void showVoice() { // 同じメソッド名で追加する
         System.out.println("水族館のスーパースターのイルカです!鳴き声は" + this.voice);
    }
}

実際に呼び出してみます。

Dolphin dolphin = new Dolphin("イルカ","イルカさん","キュー");
dolphin.showVoice();

スーパークラスに書いてある処理が上書きされて、サブクラスの処理が行われます。
出力結果は「水族館のスーパースターのイルカです!鳴き声はキュー」となります。

オーバーロードを確認しよう

オーバーロードは、同じ名前で引数が違うメソッドを定義することです。

Dolphinクラスにオーバーロードしたクラスを定義してみます。

class Dolphin {
    // ----- 省略 -----
    public void eatFish(String fishName) {
        System.out.println("イルカは" + fishName + "を食べた");
    }

    public void eatFish(String fishName1, String fishName2) {
        System.out.println("イルカは" + fishName1 + "と" + fishName2 + "を食べた");
    }

    public void eatFish(String fishName1, String fishName2, String fishName3) {
        System.out.println("イルカは" + fishName1 + "と" + fishName2 + "と" + fishName3 + "を食べた");
    }
}

このように同じメソッド名で複数定義できます。
渡された引数の数によって、使われるメソッドが変わります。

実際に実行してみます。

Dolphin dolphin = new Dolphin("イルカ","イルカさん","キュー");
dolphin.eatFish("さんま");
dolphin.eatFish("さんま", "さば");
dolphin.eatFish("さんま", "さば", "ししゃも");

この出力結果は、下記のようになります。
渡された引数の数で、出力結果が変わりましたね。

イルカはさんまを食べた
イルカはさんまとさばを食べた
イルカはさんまとさばとししゃもを食べた

また、引数の数以外に引数の型でも処理を分けてくれます。
引数の数は同じですが、型が違うので、このように追加で定義することができます。

class Dolphin {
    // ----- 省略 -----
    public void eatFish(int fishCount) {
        System.out.println("イルカは魚を" + fishCount + "つ食べた");
    }
}

数値を渡すと、こちらの引数が使われます。

Dolphin dolphin = new Dolphin("イルカ","イルカさん","キュー");
dolphin.eatFish(1);

出力結果は「イルカは魚を1つ食べた」となります。

今回確認したことをまとめると…

・スーパークラスのクラス名をサブクラスのextendsに書くことで継承できる
・サブクラスにメソッドを増やすことで、簡単に機能を拡張して追加できる
・スーパークラスのメソッドと同じ名前で、サブクラスにもメソッドを定義して上書きできる
・クラスの中で、複数同じ名前の引数違いのメソッドを作ることができる

おわりに

今回はクラスの継承を見ていきました。
継承について、なんとなくイメージが掴めたでしょうか。

このようにオブジェクト指向プログラミングでは、クラスを継承することによって効率的に処理ができるようになっています。
共通して使うものはスーパークラスに書いて、それぞれでしか使わない機能はサブクラスに書いていくと良いでしょう。

言語によって使えない機能があったりするので、実際に使用する場合はそれぞれの言語仕様を確認しましょう。

コメント

タイトルとURLをコピーしました