Java道|オーバーライドとオーバーロードの違い

同じ名前でも、意味はまったく違う。
オーバーライドとオーバーロードを見分けると、継承・多態性・メソッド設計のつながりがすっきり見えてきます。

Javaのオブジェクト指向を学んでいると、オーバーライドオーバーロード という言葉が出てきます。

名前がとても似ているので、最初は混ざりやすいです。
どちらもメソッドに関係するしくみですが、意味はかなり違います。

鬼滅の刃風にたとえると、どちらも「同じ技名のように見える場面」に関係します。
しかし、実際にやっていることは別です。

用語鬼滅の刃風にたとえると
オーバーロード同じ技名で、使い方の違う型を複数用意する
オーバーライド親から受け継いだ技を、子が自分流に作り直す

たとえば、剣士が useBreathing() という技を持っているとします。

何も指定せずに呼吸を使う版、型番号を指定する版、型番号と相手を指定する版を用意するなら、これはオーバーロードです。

一方で、親クラスの show() を、柱クラス側で柱専用の表示内容に作り直すなら、これはオーバーライドです。

つまり、似ているのは「同じメソッド名が出てくる」という点だけです。
見分けるときは、引数の形が違うのか、継承して親のメソッドを作り直しているのか を見ると整理しやすくなります。

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

まず、いちばん大切な違いを表で整理します。

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

この違いはとても重要です。

オーバーロードは、引数の違いでメソッドを使い分ける しくみです。
オーバーライドは、親クラスのメソッドを子クラスで作り直す しくみです。

見分けるときは、次の2つを見ると分かりやすいです。

見るポイント判断
引数の形が違うオーバーロード
引数の形が同じで、継承関係があるオーバーライド

鬼滅の刃風にたとえると、オーバーロードは「水の呼吸を、型番号あり・なしで使い分ける」イメージです。
オーバーライドは「鬼殺隊士の基本表示を、柱用の表示に作り直す」イメージです。

↓クリックすると拡大表示されます。

オーバーロードの考え方

オーバーロードは、同じメソッド名を使いながら、引数の数や型を変えて複数のメソッドを定義するしくみです。

鬼滅の刃風にたとえると、剣士が useBreathing という同じ名前の技を持っているとしても、使い方をいくつか用意するイメージです。

たとえば、次のような使い分けです。

メソッド意味
useBreathing()何も指定せず、基本の呼吸を使う
useBreathing(int form)型番号を指定して呼吸を使う
useBreathing(int form, String target)型番号と相手を指定して呼吸を使う

コードの形で見ると、次のようになります。

void useBreathing()
{
    System.out.println("全集中の呼吸を使う");
}

void useBreathing(int form)
{
    System.out.println("水の呼吸 " + form + "ノ型を使う");
}

void useBreathing(int form, String target)
{
    System.out.println(target + "に対して水の呼吸 " + form + "ノ型を使う");
}

どれも useBreathing という同じ名前です。
しかし、引数の形が違います。

Javaは、この引数の違いを見て、どのメソッドを呼び出すか判断します。

呼び出し実行されるメソッド
useBreathing()引数なしの useBreathing()
useBreathing(1)int を1つ受け取る useBreathing(int form)
useBreathing(10, "鬼")int と String を受け取る useBreathing(int form, String target)

つまり、オーバーロードは、
同じ役割のメソッドを、入力パターンに応じて複数用意する仕組み
です。

オーバーライドの考え方

オーバーライドは、継承したメソッドをサブクラス側で作り直すしくみです。

このとき、メソッド名だけでなく、引数の形もスーパークラスと同じである必要があります。

鬼滅の刃風にたとえると、DemonSlayer クラスに show() という「隊士の情報を表示する」共通メソッドがあるとします。

親クラスの show() は、次のような情報を表示します。

DemonSlayer の show()内容
name隊士の名前
rank階級

しかし、PillarSlayer クラスでは、それだけでは足りないかもしれません。
柱なら、担当区域も表示したいからです。

PillarSlayer の show()内容
name柱の名前
rank階級
area担当区域

このとき、PillarSlayer 側で同じ show() を定義します。

public void show()
{
    System.out.println("柱の名前は" + name + "です。");
    System.out.println("階級は" + rank + "です。");
    System.out.println("担当区域は" + area + "です。");
}

親クラスにも show() があり、子クラスにも同じ show() がある。
そして引数も同じ。

この場合、子クラスの show() は、親クラスの show() をオーバーライドしています。

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

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

オーバーロードとオーバーライドを並べると、違いがはっきりします。

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

似ているのは、メソッド名が同じという部分です。

しかし、引数の形と継承の関係を見ると、まったく違う仕組みだと分かります。

判断ポイント結論
同じメソッド名で、引数が違うオーバーロード
同じメソッド名で、引数も同じ、さらに継承関係があるオーバーライド

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

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

鬼滅の刃風にたとえると、同じ useBreathing という技でも、状況によって呼び出し方を変えたい場面です。

使い方
通常の呼吸を使うuseBreathing()
型番号を指定するuseBreathing(1)
型番号と相手を指定するuseBreathing(10, "鬼")

このように、同じ役割の処理を、引数の違いで使い分けたいときにオーバーロードが便利です。

オーバーロードが向いている考え方

判断ポイント内容
同じ名前で表したいかどれも呼吸を使う処理なので useBreathing で統一したい
入力パターンが違うか引数なし、型番号あり、対象ありなど
処理の意味が近いかどれも呼吸を使うという目的は同じ

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

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

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

鬼滅の刃風にたとえると、DemonSlayer クラスに show() という共通の情報表示メソッドがあるとします。

一般隊士なら、名前と階級を表示すれば十分です。
しかし、柱なら担当区域も表示したくなります。

このようなとき、PillarSlayer クラスで show() を作り直します。

クラスshow() の内容
DemonSlayer名前、階級を表示
PillarSlayer名前、階級、担当区域を表示

呼び出し名は同じ show() のままです。
しかし、実際の表示内容はクラスごとに変わります。

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

super. が必要になる場面

オーバーライドでは、親クラスのメソッドをまったく使わずに、子クラス側で全部書き直すこともできます。

しかし実際には、親クラスの処理を土台として使い、そのあとに子クラス独自の処理だけを追加したい場面があります。

そのときに使うのが super. です。

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

たとえば、親クラスの show() で名前と階級を表示し、子クラスではそのあとに担当区域だけを追加したい場合、次のように書けます。

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

この書き方では、まず親クラスの show() が動きます。
そのあと、子クラス独自の area を表示します。

鬼滅の刃風にたとえると、まず鬼殺隊士としての基本紹介を行い、そのあとに柱としての担当区域を追加で伝えるイメージです。

super. を使うメリット

メリット内容
親クラスの処理を再利用できる名前や階級の表示をもう一度書かなくてよい
子クラスは追加部分に集中できる担当区域の表示だけを書き足せる
共通処理の変更に強くなる親の show() を変更すれば、子側にも反映されやすい

つまり super. は、オーバーライドしたメソッドの中で、親の処理を活かしたいときに便利です。

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

super. はメソッドだけでなく、フィールドにも使えます。

これは、スーパークラスとサブクラスに同じ名前のフィールドがあるときに役立ちます。

たとえば、親クラスにも level があり、子クラスにも level があるとします。

class DemonSlayer
{
    protected int level = 1;
}

class PillarSlayer extends DemonSlayer
{
    private int level = 10;

    public void showLevel()
    {
        System.out.println("子クラスの level は" + level + "です。");
        System.out.println("親クラスの level は" + super.level + "です。");
    }
}

この場合、PillarSlayer の中で単に level と書くと、基本的には子クラス側の level を指します。

一方、super.level と書くと、親クラス側の level を指します。

書き方指すもの
level子クラス側の level
super.level親クラス側の level

鬼滅の刃風にたとえると、柱クラス自身が持つ階級値と、鬼殺隊士クラス側にある基本階級値を区別するようなイメージです。

this. と super. の違い

super. と一緒に整理したいのが this. です。

this. は、自分自身のインスタンス変数やメソッドを明示するときに使います。

たとえば、フィールドにも name があり、引数にも name がある場合、次のように書きます。

this.name = name;

この意味は次のとおりです。

書き方指すもの
this.nameこのオブジェクト自身が持つフィールド name
name引数として受け取った name

一方で、super. は親クラス側のメンバを明示します。

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

鬼滅の刃風にたとえると、this. は「今この柱自身が持っている情報」を指します。
super. は「鬼殺隊士として親クラスから受け継いだ情報」を指します。

書き方鬼滅の刃風のイメージ
this.nameこの柱自身の名前欄
super.name親クラス側の鬼殺隊士としての名前欄

this. と super. を混同しない考え方

this. と super. は、どちらも「どのメンバを指しているのか」をはっきりさせるために使います。

ただし、指す場所が違います。

使いたいもの書き方
自分自身のフィールドやメソッドthis.
親クラスのフィールドやメソッドsuper.

鬼滅の刃風にたとえると、this. は「今の隊士本人の情報を見る」ことです。
super. は「親クラスである鬼殺隊士の共通情報を見る」ことです。

この違いを押さえると、オーバーライドしたメソッドの中で、親の処理を使うのか、自分の処理を使うのかを整理しやすくなります。

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

オーバーライドでは、親クラスの処理を完全に無視して、子クラスで全部書き直すこともできます。

しかし、親の処理をそのまま使えるなら、それを活かしたほうが分かりやすくなる場面があります。

たとえば、DemonSlayer の show() が名前と階級を表示するなら、PillarSlayer の show() では次のようにできます。

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

このようにすると、

処理担当
名前と階級の表示親クラスの show()
担当区域の表示子クラスの show()

という役割分担になります。

親クラスの共通処理を使いながら、子クラス独自の処理だけを足せるので、コードがすっきりします。

鬼滅の刃風にたとえると、鬼殺隊士としての基本紹介は親クラスに任せ、柱としての担当区域だけを自分で追加する形です。

図:オーバーロードとオーバーライドの違い

↓クリックすると拡大表示されます。

この図が示していること

この図では、左側にオーバーロード、右側にオーバーライドを並べて比較しています。

左側のオーバーロードでは、useBreathing という同じ名前のメソッドが3つあります。
しかし、引数の形が違います。

メソッド引数
useBreathing()なし
useBreathing(int form)int が1つ
useBreathing(int form, String target)int と String

これがオーバーロードです。

右側のオーバーライドでは、DemonSlayer と PillarSlayer の両方に show() があります。
メソッド名も引数も同じですが、PillarSlayer 側で処理内容を作り直しています。

この図から、次の違いが分かります。

しくみ判断ポイント
オーバーロード引数の形が違う
オーバーライド継承関係があり、メソッド名と引数の形が同じ

図:super. と this. の違い

↓クリックすると拡大表示されます。

この図が示していること

この図では、this. と super. の指す場所の違いを表しています。

PillarSlayer クラスの中で this. を使うと、自分自身のメンバを指します。

書き方意味
this.name自分自身の name
this.show()自分自身の show()

一方、super. を使うと、親クラスである DemonSlayer 側のメンバを指します。

書き方意味
super.name親クラス側の name
super.show()親クラス側の show()

特にオーバーライドでは、super.show() がよく使われます。
親クラスの show() を先に実行し、そのあとで子クラス独自の表示を追加できるからです。

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

オーバーライドとオーバーロードは名前が似ていますが、見るべきポイントを決めておけば混同しにくくなります。

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

覚え方としては、次のように考えると分かりやすいです。

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

鬼滅の刃風に言うなら、

用語たとえ
オーバーロード水の呼吸を、型番号あり・なし・対象ありで使い分ける
オーバーライド鬼殺隊士の基本表示を、柱の表示に作り直す

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

オーバーロードは、同じ役割のメソッドを入力違いで増やす仕組み です。

たとえば、useBreathing()、useBreathing(int form)、useBreathing(int form, String target) のように、同じ技名で複数の使い方を用意します。

オーバーライドは、継承したメソッドを子クラスらしく作り直す仕組み です。

たとえば、DemonSlayer の show() を、PillarSlayer 側で担当区域まで表示する show() に作り直します。

そして、

書き方役割
super.親クラスのメンバを明示する
this.自分自身のメンバを明示する

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

オーバーロード、オーバーライド、super.、this. の関係が見えてくると、Javaのメソッドまわりの理解がかなり整理されます。

似た名前に振り回されず、
引数が違うのか、継承して作り直しているのか
を落ち着いて見れば、どちらの仕組みなのか判断しやすくなります。