Java入門|オーバーライドとオーバーロードの違いと使い分け

似ているようで役割はまったく違う。
オーバーライドとオーバーロードを正しく見分けると、継承と多態性のしくみがすっきりつながる。

Javaのオブジェクト指向を学んでいると、オーバーライドとオーバーロードという、とてもよく似た名前の言葉が出てきます。
どちらもメソッドに関係する重要なしくみですが、意味はかなり違います。ここがあいまいなままだと、継承の理解も、多態性の理解も少しぼんやりしてしまいます。

ドラゴンボールでたとえると、この2つはどちらも「同じ技名のように見える場面」に関係しています。けれども、中身をよく見るとまったく別の話です。

  • オーバーロードは、同じ技名でも使い方や渡す条件が違う別バージョンを用意すること
  • オーバーライドは、親から受け継いだ技を、子が自分流に作り直すこと

たとえば、悟空が「気を高める」という行動をするときに、通常状態で行う版、超サイヤ人状態で行う版、仲間と連携しながら行う版を分けて用意するなら、これはオーバーロードに近い考え方です。
一方で、親クラスにある show のような共通の技を、ベジータ専用の見せ方へ作り直すなら、それはオーバーライドです。

今回は、この2つの違いをドラゴンボールの世界観で整理しながら、さらに super. と this. の役割までつなげて解説していきます。ここが見えてくると、

  • 何がオーバーロードなのか
  • 何がオーバーライドなのか
  • サブクラスで親のメンバをどう使い分けるのか

が、かなり自然に理解できるようになります。

オーバーライドとオーバーロードは何が違うのか

まず、いちばん大切な違いをはっきりさせておきます。

用語何をするしくみかポイント
オーバーロード同じメソッド名で、引数の形式が異なるメソッドを複数定義する同じクラス内での使い分けが中心
オーバーライドサブクラスで、スーパークラスと同じメソッド名・同じ引数の形式のメソッドを定義する継承したメソッドを子クラス側で作り直す

この違いはとても重要です。
名前が似ているので混ざりやすいのですが、見分けるときはまず 引数の形が同じか違うか と、継承が関係しているか を見ると整理しやすいです。

オーバーロードの考え方

オーバーロードは、同じメソッド名を使いながら、引数の数や型を変えて複数のバリエーションを用意するしくみです。

ドラゴンボールでたとえるなら、たとえば戦士が powerUp という同じ名前の動作を持っていたとしても、

  • 何も渡さずに通常の気合いで高める
  • 数値を1つ渡して指定レベルまで高める
  • 数値と状態名を渡して、特定の形態で高める

というふうに、使い方ごとに別バージョンを用意する感じです。

大事なのは、どれもメソッド名は同じでも、Javaは引数の違いで区別していることです。

たとえばイメージとしては、こんな感覚です。

powerUp()
powerUp(int level)
powerUp(int level, String form)

これは全部 powerUp という同じ名前ですが、引数の形が違うので別メソッドとして成り立ちます。

ドラゴンボール風に言えば、
同じ「気を高める」という技名でも、発動条件や入力内容の違う技のバリエーションを用意している
というイメージです。

オーバーライドの考え方

オーバーライドは、継承したメソッドをサブクラス側で作り直すしくみです。
このとき、メソッド名も引数の形式もスーパークラスとまったく同じでなければなりません。

ドラゴンボールでたとえると、親クラスに show という「戦士の情報を表示する」共通機能があるとして、子クラスのエリートサイヤ人では、その show を自分向けに作り直す感覚です。

親クラスの show は、

  • 名前
  • 戦闘力

だけを表示するかもしれません。

でも子クラスの show では、

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

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

このとき、show という同じ名前、同じ引数の形のまま、子クラスで定義しなおします。
すると、そのオブジェクトに対して show を呼び出したときは、子クラス側の show が使われます。

つまりオーバーライドは、
親の技の型を受け継ぎながら、子が自分の役割に合わせて中身を磨き直すこと
です。

2つを並べると違いが見えやすい

ここを表で並べると、かなり整理しやすくなります。

比較項目オーバーロードオーバーライド
メソッド名同じ同じ
引数の形式異なる同じ
継承との関係なくてもよい必ず継承が関係する
目的使い方の違う複数バージョンを用意する親のメソッドを子向けに作り直す
ドラゴンボールでのイメージ同じ技名の別条件版親の技を子が自分流に上書き

この表で見ると、似ているのは「名前が同じ」という部分だけです。
それ以外はかなり違います。

だから見分け方としては、

  • 引数が違えばオーバーロード
  • 引数まで同じで、継承関係があるならオーバーライド

と覚えるとわかりやすいです。

どう使い分けるのか

違いがわかったら、次は使い分けです。

オーバーロードが向いている場面

オーバーロードは、同じ意味の操作を、いくつかの入力パターンで使いたいとき に向いています。

ドラゴンボールでいうなら、同じ battleStart という行動でも、

  • 引数なしなら通常戦闘
  • 敵の名前を渡したら特定の相手との戦闘
  • 敵の名前と戦闘力を渡したら、より詳しい戦闘準備

というように、入力の違いで使い分けたいときです。

つまりオーバーロードは、
同じ役割を、使い方の違いに応じて増やす
ためのしくみです。

オーバーライドが向いている場面

オーバーライドは、親クラスに共通メソッドがあり、それを子クラスごとに自分らしく変えたいとき に向いています。

ドラゴンボールで言えば、戦士全体に show という共通の表示機能があるけれど、

  • 悟空なら悟空らしい表示
  • ベジータならベジータらしい表示
  • 悟飯なら悟飯らしい表示

にしたいときです。

つまりオーバーライドは、
共通の呼び出し名を保ったまま、振る舞いだけをクラスごとに変える
ためのしくみです。

super. が必要になる場面

ここからは、オーバーライドしたあとに便利になる super. の話につなげます。

サブクラスでオーバーライドするとき、親クラスのメソッドをまったく使わずに全部書き直すこともできます。
でも実際には、親クラスの処理を土台として使い、そのあとに子クラス独自の処理だけを足したいことがよくあります。

そのときに使うのが super. です。
サブクラスの中で super. を付けてメソッドを呼び出すと、スーパークラスのメソッドを明示的に呼び出せます。

たとえば、親クラスの show で名前と戦闘力を表示し、子クラスではそのあとに担当エリアだけ追加したいなら、考え方はこうです。

public void show()
{
    super.show();
    System.out.println("担当エリアは" + area + "です。");
}

これなら、親クラス側の表示をそのまま再利用しながら、子クラス分だけを追加できます。
全部を書き直すより、すっきりしますね。

ドラゴンボールでたとえると、
まず親クラスの共通紹介をして、そのあとでエリート戦士としての追加情報を見せる
イメージです。

super. はフィールドにも使える

super. はメソッドだけでなく、フィールドにも使えます。
これは、スーパークラスとサブクラスに同じ名前のフィールドがあるときに役立ちます。

たとえば、親クラスにも x があり、子クラスにも x があるとします。
サブクラスの中で単に x と書くと、基本的にはサブクラス側の x を指します。
でも、親クラス側の x を使いたいときは super.x と書きます。

イメージとしてはこうです。

x = 10;
super.x = 20;

この場合、

  • x はサブクラスのフィールド
  • super.x はスーパークラスのフィールド

を意味します。

ドラゴンボールで考えるなら、
同じ「powerLabel」という名前の表示項目が親にも子にもあるときに、親のものを明示して指定する感覚です。

this. と super. の違い

ここで混ざりやすいのが this. です。

this. は、同じクラスの中で、インスタンス変数とローカル変数の名前が重複したときなどに、そのクラス自身のインスタンス変数 を明示するために使います。

たとえば、フィールドにも x があり、引数にも x があるなら、

this.x = x;

のように書きます。

この意味は、

  • this.x はインスタンス変数の x
  • x は引数の x

です。

それに対して super. は、親クラス側のメンバ を明示します。

違いを表で整理するとこうなります。

指定指すもの
this.自分のクラスのインスタンス変数やメソッド
super.スーパークラスのメンバ

つまり、

  • this. は「自分自身のどれかを明示したい」とき
  • super. は「親クラスのどれかを明示したい」とき

に使うわけです。

this. と super. をドラゴンボールで考える

ドラゴンボールでたとえると、this. は
今この戦士自身が持っている情報
をはっきり示す感じです。

一方、super. は
この戦士が親クラスから受け継いでいる共通部分
をはっきり示す感じです。

たとえばベジータ専用クラスの中で、

  • this.power は「今のベジータ自身の戦闘力情報」
  • super.power は「親クラス側にある共通の戦闘力フィールド」

というように、どの階層のものを使いたいかを明示しているわけです。

super. を使うとオーバーライドが書きやすくなる

オーバーライドでは、親クラスの処理を一部引き継ぎつつ、足りない分だけを追加したいことが多いです。
そのとき super. があると、コードがかなり書きやすくなります。

たとえば show を完全に書き直すのではなく、

  1. まず親クラスの show を使う
  2. そのあとで子クラス独自の情報を追加する

という形にできます。

この考え方の良いところは、親クラスの共通処理が変わっても、子クラス側は追加部分だけに集中しやすいことです。

ドラゴンボールで言えば、
サイヤ人共通の自己紹介は親クラスに任せ、ベジータらしい一言だけを子クラスで足すようなものです。
とても自然ですね。

オーバーライドとオーバーロードを混同しないコツ

最後に、混同しないためのコツを整理しておきます。

見分け方のコツ

確認ポイントオーバーロードオーバーライド
引数の形は違うかはいいいえ
継承が関係しているか必須ではない必須
目的は何か入力パターンを増やす親の振る舞いを子向けに変える

覚え方のコツ

  • オーバーロード
    同じ名前の技を、条件違いでいくつも用意すること
  • オーバーライド
    親から受け継いだ技を、子が自分流に作り直すこと

この2つをドラゴンボールで言いかえるなら、

  • オーバーロードは「同じ技名の派生バリエーション」
  • オーバーライドは「親の技を子が自分流に上書きしたもの」

です。

図で違いを整理する

左側では、同じ powerUp という名前でも、引数の形が違うことで別のメソッドとして並んでいる様子を見ます。これがオーバーロードです。

右側では、親クラスと子クラスの両方に show があり、子クラス側が親クラスの show を上書きしている関係を見ます。これがオーバーライドです。

そして下部の補足では、

  • super.show() で親クラスのメソッドを呼び出せること
  • this.x と super.x で、どの階層のフィールドを指しているかを区別できること

をまとめています。

この図を頭に入れておくと、名前が似ていても、2つの役割をかなりはっきり区別できるようになります。

使い分けをひとことで整理すると

ひとことで整理するなら、こうです。

  • オーバーロードは、同じ役割のメソッドを入力違いで増やすしくみ
  • オーバーライドは、継承したメソッドを子クラスらしく作り直すしくみ

そして、

  • 親の処理をそのまま活かしたいなら super.
  • 自分自身のフィールドやメソッドを明示したいなら this.

という形でつながります。

この流れが見えてくると、Javaのメソッドまわりの整理がかなりしやすくなります。
似た名前に振り回されず、どの場面で何を使うのかを落ち着いて判断できるようになります。