Java入門|メソッドのオーバーライド(上書き)のしくみ

同じ技の名前でも、使う戦士が変われば中身も変わる。
オーバーライドを知ると、継承がただの引き継ぎではなく、自分流への進化だと見えてくる。

継承を学ぶと、スーパークラスで用意したフィールドやメソッドを、サブクラスが受け継いで使えることがわかってきます。
ここまででもかなり便利ですが、継承の本当のおもしろさは、ただ受け継ぐだけでは終わらないところにあります。

Javaでは、サブクラスで新しいメソッドを追加するだけでなく、スーパークラスとまったく同じ名前・同じ引数の形を持つメソッドを、サブクラス側であらためて定義できます。すると、サブクラスのオブジェクトに対してそのメソッドを呼び出したとき、親クラスのものではなく、子クラスで定義したほうが使われます。これがオーバーライドです。

ドラゴンボールでたとえると、この考え方はとても自然です。
たとえば「戦い方を見せる」という共通の行動があったとしても、悟空とベジータと悟飯では、その見せ方がまったく同じになるわけではありません。戦士として共通の土台はあっても、実際の表現はそれぞれ違います。

  • 親クラスには共通の見せ方がある
  • 子クラスでは、自分の特徴に合わせてその見せ方を作り直せる

このしくみが、オーバーライドの大切なポイントです。

今回は「メソッドのオーバーライドのしくみ」を、ドラゴンボールの戦士たちに置きかえながら、やわらかく丁寧に整理していきます。特に、

  • オーバーライドとは何か
  • なぜサブクラス側のメソッドが呼ばれるのか
  • スーパークラスとサブクラスの show の違い
  • 継承とオーバーライドの関係

を順番に見ていきます。

オーバーライドとは何か

オーバーライドは、スーパークラスにあるメソッドと同じ名前、同じ引数の形を持つメソッドを、サブクラスでも定義しなおすことです。
そして、そのサブクラスのオブジェクトに対してメソッドを呼び出すと、スーパークラスのものではなく、サブクラス側で定義したメソッドが働きます。

言いかえると、オーバーライドは
親クラスの共通機能を、子クラスの個性に合わせて上書きするしくみ
です。

ドラゴンボールで考えると、共通の「戦士」という土台があっても、実際の見せ方や戦い方は戦士ごとに違います。
親クラスに show という共通メソッドがあったとしても、子クラスでは自分専用の show を作りたくなる場面があります。

たとえば、サイヤ人クラスでは

  • 名前
  • 戦闘力

だけを表示する show があるとします。

でも、エリートサイヤ人クラスではそれに加えて

  • 担当エリア

も表示したいかもしれません。

このとき、サブクラス側で同じ show を定義しなおすと、親の show をそのまま使うのではなく、自分向けに作り直した show が使われるようになります。
これがオーバーライドです。

同じメソッド名なら何でもオーバーライドになるのか

ここで大事なのは、ただ同じ名前を書けばよいわけではないことです。

オーバーライドとして成立するには、スーパークラスとサブクラスで、

  • メソッド名
  • 引数の数
  • 引数の型

が同じである必要があります。

たとえば親クラスに

show()

があるなら、子クラスでも

show()

と同じ形で定義します。

これによって Java は、
「これはまったく別の新メソッドではなく、親のメソッドを子クラス向けに作り直したものだ」
と判断します。

ドラゴンボールの感覚でいえば、
「戦士の情報を見せる」という同じ役目を持ちながら、その内容だけをキャラクターごとに変える
というイメージです。

継承したメソッドをそのまま使う場合との違い

継承では、スーパークラスのメソッドはそのままサブクラスでも使えます。
何も定義しなければ、親クラスのメソッドがそのまま働きます。

でも、子クラスに独自の事情があると、それでは足りないことがあります。

たとえばサイヤ人クラスの show が

  • 名前
  • 戦闘力

しか表示しないなら、エリートサイヤ人の情報表示としては少し物足りません。
エリート戦士なら、担当エリアまで見せたいはずです。

そこでサブクラス側で show を定義しなおすと、

  • 親の共通ルールを土台にしつつ
  • 子の個性に合わせた表示に変えられる

ようになります。

つまりオーバーライドは、継承をより実用的にするためのしくみでもあります。

ドラゴンボールで考えるオーバーライド

ドラゴンボールで整理すると、オーバーライドはとてもわかりやすいです。

たとえば、スーパークラス Saiyan に

  • 戦士の情報を表示する show()

があるとします。

この show() は、サイヤ人全体に共通する基本表示です。
でも EliteSaiyan では、エリート戦士としての追加情報もあります。

だから EliteSaiyan では、同じ show() という名前のまま、

  • 名前
  • 戦闘力
  • 担当エリア

を表示するように作り直したくなります。

ここで大事なのは、メソッド名は同じでも、中身は違ってよいということです。
むしろ、それぞれのクラスらしい中身に変えることが、オーバーライドの本質です。

サンプルプログラム:オーバーライド

ここでは、ドラゴンボールの世界観で、オーバーライドの動きを確認していきます。親クラス Saiyan にある show() を、子クラス EliteSaiyan 側で作り直している構成です。

ファイル名:Sample4.java

class Saiyan
{
    protected String name;
    protected int power;

    public Saiyan()
    {
        name = "戦士なし";
        power = 0;
        System.out.println("サイヤ人を作成しました。");
    }

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

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

class EliteSaiyan extends Saiyan
{
    private int area;

    public EliteSaiyan()
    {
        area = 0;
        System.out.println("エリートサイヤ人を作成しました。");
    }

    public void setArea(int a)
    {
        area = a;
        System.out.println("担当エリアを" + area + "にしました。");
    }

    public void show()
    {
        System.out.println("エリートサイヤ人の名前は" + name + "です。");
        System.out.println("戦闘力は" + power + "です。");
        System.out.println("担当エリアは" + area + "です。");
    }
}

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

        warrior1.setSaiyan("ベジータ", 18000);
        warrior1.setArea(7);

        warrior1.show();
    }
}

ここで注目する show の関係

このプログラムでは、Saiyan にも EliteSaiyan にも show() があります。

クラスshow の役割
Saiyan名前と戦闘力を表示する
EliteSaiyan名前、戦闘力、担当エリアを表示する

この2つは、メソッド名が同じで、引数もありません。
つまり、オーバーライドの条件を満たしています。

ここで warrior1.show(); を実行すると、呼ばれるのは Saiyan の show ではなく、EliteSaiyan の show です。
なぜなら、warrior1 が EliteSaiyan のオブジェクトだからです。

この動きが、オーバーライドそのものです。

なぜサブクラス側の show が呼ばれるのか

継承では、サブクラスはスーパークラスのメンバを受け継ぎます。
しかし、同じ名前・同じ引数のメソッドを子クラス側で定義すると、その子クラス版のメソッドが優先されます。

つまり、Java は
「このオブジェクトはエリートサイヤ人なのだから、情報表示もエリートサイヤ人版を使おう」
と判断しているわけです。

ドラゴンボールでたとえると、

  • 戦士全体の基本紹介は親クラスにある
  • でもエリート戦士なら、エリート戦士らしい紹介のしかたをする

という感覚です。

共通の技の名前が show() だとしても、使う戦士が変われば、見せる内容も変わる。
これがオーバーライドの面白いところです。

親の show と子の show の違い

この違いを表で見ると、かなり整理しやすくなります。

比較項目Saiyan の show()EliteSaiyan の show()
メソッド名show()show()
引数なしなし
表示内容名前、戦闘力名前、戦闘力、担当エリア
使われる場面サイヤ人として表示したいときエリートサイヤ人として表示したいとき

ここで注目したいのは、名前と引数の形は同じなのに、表示する内容が広がっていることです。

つまり子クラスは、親クラスの役割を引き継ぎつつ、自分に必要な分だけ中身を変えています。
これが「上書き」という言葉のイメージにぴったりです。

オーバーライドは「親を消す」ことではない

オーバーライドという言葉から、親クラスのメソッドが完全に消えてしまうように感じるかもしれません。
でも実際にはそうではありません。

親クラスには親クラスの show() がちゃんとあります。
ただ、子クラスのオブジェクトに対して show() を呼び出したときには、子クラス側の show() が使われる、ということです。

つまり、親のメソッドがなくなるのではなく、
子クラスにふさわしい振る舞いへ切り替わる
という理解が自然です。

ドラゴンボールでいえば、サイヤ人全体の共通ルールは残っています。
そのうえで、エリートサイヤ人が自分流の見せ方をしているだけです。

protected がここで役立っている理由

このサンプルでは、name と power が protected になっています。

protected String name;
protected int power;

これにより、サブクラス EliteSaiyan の show() の中から、name と power を直接使えます。
もしこれらが private なら、子クラス側の show() からは直接アクセスできません。

つまり今回のオーバーライドは、前の内容で学んだ protected のしくみともつながっています。

  • 親クラスの情報を子クラスから使えるようにする
  • そのうえで子クラス専用の表示に作り変える

この流れがあるからこそ、EliteSaiyan の show() は自然に書けるわけです。

実行の流れを整理する

このプログラムの処理の流れを順番に見ると、次のようになります。

順番処理内容
1new EliteSaiyan()先に Saiyan()、次に EliteSaiyan() が動く
2setSaiyan("ベジータ", 18000)親クラスのメソッドで名前と戦闘力を設定する
3setArea(7)子クラスのメソッドで担当エリアを設定する
4show()子クラスで上書きした show() が呼ばれる

この最後の 4 番目が、今回の中心です。
show() という同じ呼び出しをしていても、実際にはサブクラス版が動いています。

ここに、オーバーライドのしくみがきれいに表れています。

図でオーバーライドの流れをつかむ

文章だけでも理解できますが、オーバーライドは図で見るとさらにわかりやすくなります。

この図では、親クラスと子クラスの両方に show() があることを確認します。
そして右側の warrior1.show() という呼び出しが、親ではなく子クラスの show() に向かっているところがポイントです。

つまり、同じ show() という名前でも、オブジェクトがエリートサイヤ人なら、エリートサイヤ人版の show() が働くわけです。

オーバーライドが「名前の重複」ではなく、「子クラス側への切り替え」であることが分ります。

オーバーライドがあると何がうれしいのか

オーバーライドの良さは、共通の名前のまま、クラスごとにふさわしい振る舞いを持たせられることです。

たとえば show() という名前を共通にしておけば、どの戦士クラスでも
「情報を表示する」
という役割がひと目でわかります。

そのうえで、中身はそれぞれのクラスに合わせて変えられます。

良い点内容
共通の名前で扱える役割が統一されてわかりやすい
中身は個別に変えられるクラスごとの特徴を表現しやすい
継承と相性がよい親の共通性と子の個性を両立できる

ドラゴンボールでいえば、みんな戦士ではあるけれど、見せ場はそれぞれ違う。
その違いを、同じメソッド名のまま表現できるのがオーバーライドです。

オーバーライドは多態性につながる土台でもある

今回の中心はオーバーライドそのものですが、この考え方はあとで多態性にもつながっていきます。

なぜなら、同じメソッド名で呼び出しても、実際にはオブジェクトごとに違う処理が動くからです。
今回は EliteSaiyan の show() が呼ばれるところまでを見ていますが、この発想は、将来いろいろな戦士クラスをまとめて扱うときにも役立ちます。

つまりオーバーライドは、単なる上書きではなく、
共通の呼び出しと個別の振る舞いを両立させる大事な仕組み
でもあります。

ドラゴンボールで感覚的に整理する

最後に、ドラゴンボールの感覚で整理してみましょう。

Saiyan クラスには、戦士全体に共通する show() があります。
これは、基本的な自己紹介のようなものです。

EliteSaiyan クラスには、同じ show() という名前を持ちながら、より詳しい自己紹介があります。
エリート戦士なのですから、担当エリアまで見せるほうが自然です。

このとき、

  • メソッド名は同じ
  • 役割も同じく「情報を表示する」
  • でも中身は子クラス向けに作り直されている

という状態になります。

これがオーバーライドです。

ひとことで言えば、
親の技の型を受け継ぎながら、子が自分の戦い方に合わせて技を磨き直すこと
とも言えます。

この感覚がつかめると、オーバーライドは文法の暗記ではなく、継承の中で子クラスらしさを表現するための自然な仕組みとして理解できるようになります。