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

同じ名前の技でも、増やすのか、作り直すのかで意味は大きく変わります。オーバーロードとオーバーライドを見分けると、メソッド設計・継承・ポリモーフィズムの関係がすっきり見えてきます。

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

名前がとても似ているので、最初は混ざりやすいです。

どちらもメソッドに関係するしくみです。
どちらも同じメソッド名が出てくることがあります。

しかし、意味はまったく違います。

ドラゴンボールの世界観でたとえると、どちらも「同じ技名が出てくる場面」に関係します。

たとえば、サイヤ人戦士が useKiBlast() という技を持っているとします。

何も指定せずに気弾を放つ版。
威力を指定して気弾を放つ版。
威力と相手を指定して気弾を放つ版。

このように、同じ技名で使い方の違う型を複数用意するなら、これはオーバーロードです。

一方で、親クラス SaiyanWarrior にある show() を、子クラス SuperSaiyanWarrior 側でスーパーサイヤ人専用の表示内容に作り直すなら、これはオーバーライドです。

つまり、似ているのは「同じメソッド名が出てくる」という点だけです。

見分けるときは、次の2つを見ると整理しやすくなります。

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

オーバーロードは、同じ名前の技を、入力パターンに応じて複数用意するしくみです。

オーバーライドは、親から受け継いだ技を、子クラスが自分用に作り直すしくみです。

この記事では、オーバーロード、オーバーライド、super.、this. の違いを、ドラゴンボールの世界観で、やわらかく整理していきます。特に、引数の形が違うのか、継承して作り直しているのかを軸にして、2つの違いを見分けられるように解説します。

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

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

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

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

オーバーロードは、引数の違いでメソッドを使い分けるしくみです。

オーバーライドは、親クラスのメソッドを子クラスで作り直すしくみです。

ドラゴンボール風にたとえると、次のようになります。

用語ドラゴンボール風にたとえると
オーバーロード同じ技名で、威力指定あり・なし・相手指定ありなど、使い方を増やす
オーバーライド親から受け継いだ技を、子クラスが自分流に作り直す

たとえば、useKiBlast() という気弾技があるとします。

引数なしで放つ。
威力を指定して放つ。
威力と相手を指定して放つ。

これは、同じ技名で複数の使い方を用意しているので、オーバーロードです。

一方、SaiyanWarrior の show() を、SuperSaiyanWarrior 側で変身段階まで表示する show() に作り直すなら、これはオーバーライドです。

同じ名前が出てきても、やっていることは違います。

判断するときは引数と継承を見る

オーバーロードとオーバーライドを見分けるときは、次の2点を確認すると分かりやすいです。

確認することオーバーロードオーバーライド
メソッド名同じ同じ
引数の形式違う同じ
継承関係なくてもよい必ず関係する
主な目的使い方の違う複数バージョンを用意する親のメソッドを子向けに作り直す

つまり、メソッド名が同じだけでは判断できません。

引数の形が違うのか。
それとも、継承関係の中で同じ形のメソッドを作り直しているのか。

ここを見ることが大切です。

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

ドラゴンボール風に言うと、同じ技名でも、型を増やしているのか、親の技を自分流に磨き直しているのかを見分けるイメージです。

図:オーバーロードとオーバーライドの基本比較

この図が示していること

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

左側のオーバーロードでは、useKiBlast という同じ名前のメソッドが3つあります。

ただし、それぞれ引数の形が違います。

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

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

右側のオーバーライドでは、SaiyanWarrior と SuperSaiyanWarrior の両方に show() があります。

メソッド名も引数も同じですが、SuperSaiyanWarrior 側で処理内容を作り直しています。

この図から分かるのは、オーバーロードは引数の形が違うこと、オーバーライドは継承関係があり、同じ形のメソッドを子クラスで作り直すことです。

オーバーロードの考え方

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

ドラゴンボール風にたとえると、サイヤ人戦士が useKiBlast という同じ名前の技を持っていても、状況によって使い方を変えるイメージです。

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

メソッド意味
useKiBlast()何も指定せず、基本の気弾を放つ
useKiBlast(int power)威力を指定して気弾を放つ
useKiBlast(int power, String target)威力と相手を指定して気弾を放つ

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

class SaiyanWarrior
{
    void useKiBlast()
    {
        System.out.println("基本の気弾を放つ");
    }

    void useKiBlast(int power)
    {
        System.out.println("威力" + power + "の気弾を放つ");
    }

    void useKiBlast(int power, String target)
    {
        System.out.println(target + "に対して威力" + power + "の気弾を放つ");
    }
}

どれも useKiBlast という同じ名前です。

しかし、引数の形が違います。

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

呼び出し実行されるメソッド
useKiBlast()引数なしの useKiBlast()
useKiBlast(100)int を1つ受け取る useKiBlast(int power)
useKiBlast(300, "敵戦士")int と String を受け取る useKiBlast(int power, String target)

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

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

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

ドラゴンボール風にたとえると、同じ useKiBlast という技でも、状況によって呼び出し方を変えたい場面です。

使い方
通常の気弾を放つuseKiBlast()
威力を指定するuseKiBlast(100)
威力と相手を指定するuseKiBlast(300, "敵戦士")

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

判断ポイント内容
同じ名前で表したいかどれも気弾を放つ処理なので useKiBlast で統一したい
入力パターンが違うか引数なし、威力あり、相手指定ありなど
処理の意味が近いかどれも気弾を使うという目的は同じ

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

オーバーライドの考え方

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

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

ドラゴンボール風にたとえると、SaiyanWarrior クラスに show() という「戦士の情報を表示する」共通メソッドがあるとします。

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

SaiyanWarrior の show()内容
name戦士の名前
styleName流派

しかし、SuperSaiyanWarrior クラスでは、それだけでは足りないかもしれません。

スーパーサイヤ人なら、変身段階も表示したいからです。

SuperSaiyanWarrior の show()内容
nameスーパーサイヤ人の名前
styleName流派
transformationLevel変身段階

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

class SaiyanWarrior
{
    protected String name;
    protected String styleName;

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

class SuperSaiyanWarrior extends SaiyanWarrior
{
    private int transformationLevel;

    public void show()
    {
        System.out.println("スーパーサイヤ人の名前は" + name + "です。");
        System.out.println("流派は" + styleName + "です。");
        System.out.println("変身段階は" + transformationLevel + "です。");
    }
}

親クラスにも show() があり、子クラスにも同じ show() があります。

そして引数も同じです。

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

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

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

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

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

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

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

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

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

ドラゴンボール風にたとえると、SaiyanWarrior クラスに show() という共通の情報表示メソッドがある場面です。

普通のサイヤ人戦士なら、名前と流派を表示すれば十分です。

しかし、スーパーサイヤ人戦士なら、変身段階も表示したくなります。

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

クラスshow() の内容
SaiyanWarrior名前、流派を表示
SuperSaiyanWarrior名前、流派、変身段階を表示

呼び出し名は同じ show() のままです。

しかし、実際の表示内容はクラスごとに変わります。

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

super. が必要になる場面

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

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

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

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

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

class SaiyanWarrior
{
    protected String name;
    protected String styleName;

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

class SuperSaiyanWarrior extends SaiyanWarrior
{
    private int transformationLevel;

    public void show()
    {
        super.show();
        System.out.println("変身段階は" + transformationLevel + "です。");
    }
}

この書き方では、まず親クラスの show() が動きます。

そのあと、子クラス独自の transformationLevel を表示します。

ドラゴンボール風にたとえると、まずサイヤ人戦士としての基本紹介を行い、そのあとにスーパーサイヤ人としての変身段階を追加で伝えるイメージです。

super. を使うメリット

super. を使うと、親クラスの処理を再利用できます。

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

たとえば、名前と流派の表示形式を変えたい場合、親クラスの show() を修正すれば、その処理を super.show() で使っている子クラス側にも反映されやすくなります。

もちろん、子クラス側で完全に違う表示にしたい場合は、super.show() を使わずに全部書き直しても構いません。

大切なのは、親の処理を活かしたいときには super. が使えるということです。

図:super. で親の処理を活かす

この図が示していること

この図では、SuperSaiyanWarrior の show() の中で super.show() を呼び出し、親クラス SaiyanWarrior の show() を再利用している様子を表しています。

SaiyanWarrior.show() は、名前と流派を表示します。

SuperSaiyanWarrior.show() は、まず super.show() で親の表示処理を使います。

そのあと、変身段階を追加で表示します。

ここから分かるのは、オーバーライドしたメソッドの中でも、親クラスの処理を活かせるということです。

親クラスが共通情報を担当し、子クラスが追加情報を担当することで、コードの重複を減らしやすくなります。

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

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

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

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

class SaiyanWarrior
{
    protected int level = 1;
}

class SuperSaiyanWarrior extends SaiyanWarrior
{
    private int level = 10;

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

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

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

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

ドラゴンボール風にたとえると、スーパーサイヤ人としての覚醒レベルと、サイヤ人戦士としての基本レベルを区別するようなイメージです。

同じ level という名前があっても、super. を付けることで、親クラス側の level だと明示できます。

this. と super. の違い

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

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

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

class SaiyanWarrior
{
    private String name;

    public void setName(String name)
    {
        this.name = name;
    }
}

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

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

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

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

ドラゴンボール風にたとえると、this. は今この戦士自身が持っている情報を指します。

super. はサイヤ人戦士として親クラスから受け継いだ情報を指します。

書き方ドラゴンボール風のイメージ
this.nameこのスーパーサイヤ人自身の名前欄
super.name親クラス側のサイヤ人戦士としての名前欄
this.show()自分自身の show()
super.show()親クラス側の show()

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

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

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

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

図:this. と super. の違い

以下の条件でイラストを作成してください。

カラーイラスト、16:9横長。白と青を基調にした学習教材風デザイン。ドラゴンボール風のアニメ調。背景は未来的なサイヤ人継承解析ルーム。青いホログラム、スカウター風UI、thisカード、superカード、親子クラスパネルを配置する。

画面中央に大きく「this. と super. の違い」と表示する。

画面左に「this.」カードを配置し、次を表示する。

「自分自身のメンバ」
「this.name」
「this.show()」
「SuperSaiyanWarrior 側を指す」

画面右に「super.」カードを配置し、次を表示する。

「親クラスのメンバ」
「super.name」
「super.show()」
「SaiyanWarrior 側を指す」

画面中央に親子クラスパネルを配置する。

上段に親クラスカード
「SaiyanWarrior」
「name」
「show()」

下段に子クラスカード
「SuperSaiyanWarrior extends SaiyanWarrior」
「name」
「show()」

this. から子クラスカードへ青い矢印を伸ばす。
super. から親クラスカードへ青い矢印を伸ばす。

画面下部に比較カードを配置し、次を表示する。

「this.:今のオブジェクト自身」
「super.:親クラス側のメンバ」

右下に小さな悟空系戦士アイコンを配置する。ワインレッドの道着で、右胸に「亀」の丸いマークを入れる。髪型と顔、特に目元は原作のドラゴンボールのキャラクターに似ていない完全オリジナルにする。

左下に小さなベジータ系戦士アイコンを配置する。サイヤ人の戦闘服風のオリジナル衣装で、右胸に「惑」の丸いマークを入れる。髪型と顔、特に目元は原作キャラクターに似せない。

中央下に小さな悟飯系戦士アイコンを配置する。紺色の道着で、右胸に「神」の丸いマークを入れる。髪型と顔、特に目元は原作キャラクターに似せない。

戦士以外のオブジェクトも、それぞれを表す小さなアイコンで描く。

下部に「this. は自分自身、super. は親クラス側を明示する」と大きく表示する。

文字は読みやすく、背景やアイコンと重ならないように整理する。

この図が示していること

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

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

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

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

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

特にオーバーライドでは、super.show() がよく使われます。

親クラスの show() を先に実行し、そのあとで子クラス独自の表示を追加できるからです。

この図から分かるのは、this. は今のオブジェクト自身、super. は親クラス側を明示するための書き方だということです。

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

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

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

たとえば、SaiyanWarrior の show() が名前と流派を表示するなら、SuperSaiyanWarrior の show() では次のようにできます。

public void show()
{
    super.show();
    System.out.println("変身段階は" + transformationLevel + "です。");
}

このようにすると、役割分担がはっきりします。

処理担当
名前と流派の表示親クラスの show()
変身段階の表示子クラスの show()

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

ドラゴンボール風にたとえると、サイヤ人戦士としての基本紹介は親クラスに任せ、スーパーサイヤ人としての変身段階だけを自分で追加する形です。

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

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

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

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

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

ドラゴンボール風に言うなら、次のようになります。

用語たとえ
オーバーロード気弾技を、威力指定あり・なし・相手指定ありで使い分ける
オーバーライドサイヤ人戦士の基本表示を、スーパーサイヤ人の表示に作り直す

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

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

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

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

たとえば、SaiyanWarrior の show() を、SuperSaiyanWarrior 側で変身段階まで表示する show() に作り直します。

そして、this. と super. は次のように整理できます。

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

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

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