Java超|メソッドのオーバーロード

同じ技名でも、渡す内容で動きが変わる。オーバーロードを使えば、メソッド名を増やしすぎず、自然で使いやすいクラスを作れます。

クラスを使ってプログラムを書いていると、似たような処理をいくつか用意したくなることがあります。

たとえば、サイヤ人戦士の状態を設定するときに、次のような場面が考えられます。

戦闘力だけを変更したい。
気の量だけを変更したい。
戦闘力と気の量をまとめて変更したい。

このような処理は、どれも「戦士の状態を設定する」という同じ目的を持っています。

しかし、それぞれにまったく別のメソッド名をつけると、使う側は覚える名前が増えてしまいます。

たとえば、戦闘力用、気の量用、両方用で別々の名前をつけると、どれを使えばよいのか少し迷いやすくなります。

そこでJavaには、同じ名前のメソッドを、引数の違いで使い分けるしくみがあります。

これが、メソッドのオーバーロードです。

ドラゴンボールの世界観でたとえると、同じ「戦士状態を整える技」でも、渡す内容によって発動する形が変わるイメージです。

戦闘力だけ渡せば、戦闘力を整える。
気の量だけ渡せば、気の量を整える。
戦闘力と気の量を両方渡せば、両方をまとめて整える。

このように、同じ技名を使いながら、状況に合わせて処理を使い分けられるのがオーバーロードです。

オーバーロードとは

オーバーロードとは、同じクラスの中で、同じ名前のメソッドを複数定義することです。

ただし、完全に同じ形のメソッドを複数書けるわけではありません。

大切な条件があります。

引数の型が違う。
または、引数の個数が違う。

このどちらかの違いが必要です。

たとえば、次の3つはオーバーロードとして成り立ちます。

setSaiyan(int p)
setSaiyan(double e)
setSaiyan(int p, double e)

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

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

メソッド引数意味
setSaiyan(int p)int 1個戦闘力だけを設定する
setSaiyan(double e)double 1個気の量だけを設定する
setSaiyan(int p, double e)int と double の2個戦闘力と気の量をまとめて設定する

Javaは、呼び出すときに渡された引数を見て、どのメソッドを使うべきか判断します。

つまり、メソッド名だけで判断しているのではなく、引数の型や個数も見て判断しているのです。

ドラゴンボールでたとえると、同じ技名を状況で使い分ける

ドラゴンボールでたとえると、オーバーロードは「同じ技名だけれど、込める情報によって効果が変わる技」です。

たとえば、戦士の状態を整える setSaiyan という技があるとします。

しかし、戦闘前の状況によって、整えたい内容は変わります。

状況渡す情報実行したい処理
戦闘力だけ整えたい10000戦闘力を10000にする
気の量だけ整えたい20.5気の量を20.5にする
両方まとめて整えたい10000, 20.5戦闘力と気の量をまとめて設定する

この3つは、細かい処理は違います。
しかし、目的としてはどれも「戦士の状態を設定する」ことです。

だから、全部を setSaiyan という同じ名前でまとめると、使う側にとって自然です。

ドラゴンボールで言えば、同じ流派の技を、型や状況に応じて使い分けるような感覚です。

同じ名前でも、引数が違えば別のメソッドとして扱える

Javaでは、メソッドを見分けるときに、メソッド名だけでなく引数も見ます。

そのため、同じ名前でも、引数の型や個数が違えば、別のメソッドとして定義できます。

たとえば、次の3つはすべて別のメソッドとして扱われます。

public void setSaiyan(int p)

public void setSaiyan(double e)

public void setSaiyan(int p, double e)

見た目は似ていますが、引数の形が違います。

呼び出し方Javaが選ぶメソッド
goku.setSaiyan(10000)setSaiyan(int p)
goku.setSaiyan(20.5)setSaiyan(double e)
goku.setSaiyan(10000, 20.5)setSaiyan(int p, double e)

このように、Javaは渡された値の型や個数をもとに、呼び出すメソッドを選びます。

使う側は、状態を設定したいときは setSaiyan を使う、と覚えればよくなります。
そのうえで、渡す値によって適切な処理が選ばれます。

図:同じ名前でも引数で選ばれる

この図が示していること

この図では、setSaiyan という同じ名前のメソッドが、引数の違いによって3種類に分かれている様子を表しています。

int 1個なら戦闘力だけを設定します。
double 1個なら気の量だけを設定します。
int と double の2個なら、戦闘力と気の量をまとめて設定します。

ここから分かるのは、Javaではメソッド名だけでなく、引数の型や個数を見て、どのメソッドを呼ぶか判断しているということです。

オーバーロードの動きを確認する

実際のプログラムで、オーバーロードの動きを見ていきましょう。

ファイル名:Sample3.java

class Saiyan
{
    private int power;
    private double kiEnergy;

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

    public void setSaiyan(double e)
    {
        kiEnergy = e;
        System.out.println("気の量を" + kiEnergy + "にしました。");
    }

    public void setSaiyan(int p, double e)
    {
        power = p;
        kiEnergy = e;
        System.out.println("戦闘力を" + power + "に気の量を" + kiEnergy + "にしました。");
    }

    public void show()
    {
        System.out.println("戦闘力は" + power + "です。");
        System.out.println("気の量は" + kiEnergy + "です。");
    }
}

class Sample3
{
    public static void main(String[] args)
    {
        Saiyan goku = new Saiyan();

        goku.setSaiyan(10000, 20.5);
        goku.show();

        System.out.println("戦闘力だけ変更します。");
        goku.setSaiyan(12000);
        goku.show();

        System.out.println("気の量だけ変更します。");
        goku.setSaiyan(30.5);
        goku.show();
    }
}

このプログラムでは、Saiyan クラスの中に setSaiyan という同じ名前のメソッドが3つあります。

public void setSaiyan(int p)

public void setSaiyan(double e)

public void setSaiyan(int p, double e)

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

3つとも同じ名前ですが、引数の型や個数が違います。

メソッド引数の違い処理内容
setSaiyan(int p)int 1個戦闘力だけを設定
setSaiyan(double e)double 1個気の量だけを設定
setSaiyan(int p, double e)int と double の2個戦闘力と気の量を設定

最初の呼び出しでは、引数が2個のメソッドが使われる

main メソッドの最初では、次のように呼び出しています。

goku.setSaiyan(10000, 20.5);

ここでは、実引数が2つあります。

10000 は int 型です。
20.5 は double 型です。

そのため、Javaは次のメソッドを選びます。

public void setSaiyan(int p, double e)

このメソッドでは、戦闘力と気の量の両方を設定します。

power = p;
kiEnergy = e;

つまり、次のように値が入ります。

フィールド入る値
power10000
kiEnergy20.5

そのあとで show() を呼び出します。

goku.show();

すると、戦闘力と気の量が表示されます。

戦闘力を10000に気の量を20.5にしました。
戦闘力は10000です。
気の量は20.5です。

ドラゴンボールでたとえると、戦闘前に「戦闘力10000、気の量20.5」とまとめて整えている場面です。

次は int 1個なので、戦闘力だけ変わる

次に、次の処理があります。

System.out.println("戦闘力だけ変更します。");
goku.setSaiyan(12000);
goku.show();

ここで渡しているのは 12000 だけです。

12000 は int 型なので、Javaは次のメソッドを選びます。

public void setSaiyan(int p)

このメソッドでは、power だけを変更します。

power = p;

そのため、戦闘力は 12000 に変わります。

しかし、kiEnergy はこのメソッドでは変更していません。
そのため、気の量は前の 20.5 のままです。

項目呼び出し前呼び出し後
power1000012000
kiEnergy20.520.5

実行結果は次のようになります。

戦闘力だけ変更します。
戦闘力を12000にしました。
戦闘力は12000です。
気の量は20.5です。

同じ setSaiyan という名前を使っているのに、int 1個を渡したことで、戦闘力だけを変更する処理が選ばれています。

ドラゴンボールでたとえると、気の量はそのままにして、戦闘力だけを高めている状態です。

次は double 1個なので、気の量だけ変わる

さらに次の処理を見ます。

System.out.println("気の量だけ変更します。");
goku.setSaiyan(30.5);
goku.show();

ここで渡しているのは 30.5 だけです。

30.5 は double 型なので、Javaは次のメソッドを選びます。

public void setSaiyan(double e)

このメソッドでは、kiEnergy だけを変更します。

kiEnergy = e;

そのため、気の量は 30.5 に変わります。

一方で、power はこのメソッドでは変更していません。
そのため、戦闘力は 12000 のままです。

項目呼び出し前呼び出し後
power1200012000
kiEnergy20.530.5

実行結果は、次のようになります。

気の量だけ変更します。
気の量を30.5にしました。
戦闘力は12000です。
気の量は30.5です。

ここでも、メソッド名は同じ setSaiyan です。

しかし、渡した引数が double 1個なので、気の量だけを変更するメソッドが選ばれました。

ドラゴンボールでたとえると、戦闘力はそのままにして、気の量だけを高め直したようなイメージです。

実行結果で全体の流れを見る

Sample3.java の実行結果をまとめると、次のようになります。

戦闘力を10000に気の量を20.5にしました。
戦闘力は10000です。
気の量は20.5です。
戦闘力だけ変更します。
戦闘力を12000にしました。
戦闘力は12000です。
気の量は20.5です。
気の量だけ変更します。
気の量を30.5にしました。
戦闘力は12000です。
気の量は30.5です。

この結果から、同じ setSaiyan という名前でも、渡す引数によって違う処理が実行されていることが分かります。

呼び出し選ばれる処理変わる値
setSaiyan(10000, 20.5)int, double の2個を受け取るメソッド戦闘力と気の量
setSaiyan(12000)int 1個を受け取るメソッド戦闘力
setSaiyan(30.5)double 1個を受け取るメソッド気の量

これが、オーバーロードの基本的な動きです。

1つのメソッド名を覚えるだけでよい

オーバーロードの便利さは、使う側から見るとよく分かります。

もしオーバーロードを使わないなら、次のように別々の名前を考えるかもしれません。

目的別名で分ける場合の例
戦闘力を設定するsetPower
気の量を設定するsetKiEnergy
戦闘力と気の量を設定するsetPowerAndKiEnergy

もちろん、このように名前を分けてもプログラムは作れます。

ただ、似た目的の処理なのにメソッド名が増えると、使う側は覚えることが増えます。

一方、オーバーロードを使えば、状態を設定したいときは setSaiyan を使う、と考えられます。

方式覚える名前使い分け
別名で分けるsetPower、setKiEnergy、setPowerAndKiEnergy名前で使い分ける
オーバーロードsetSaiyan引数で使い分ける

ドラゴンボールでたとえると、似た技に全部バラバラの名前をつけるより、同じ流派の技としてまとめ、型の違いで使い分けるほうが覚えやすいということです。

似た役割の処理を1つの名前にまとめられる

オーバーロードの本質は、同じ名前を使えることだけではありません。

大切なのは、似た役割を持つ処理を、1つの名前にまとめられることです。

Sample3.java の setSaiyan は、どれも「戦士の状態を設定する」という共通の目的を持っています。

メソッド細かい役割共通する目的
setSaiyan(int p)戦闘力だけ設定戦士の状態を整える
setSaiyan(double e)気の量だけ設定戦士の状態を整える
setSaiyan(int p, double e)両方設定戦士の状態を整える

このように、同じ目的を持つ処理を同じ名前にまとめると、クラス全体が読みやすくなります。

「設定する処理は setSaiyan にまとまっている」と分かるからです。

これは、クラスを使う人にも、クラスを読む人にもやさしい設計です。

図:3つの setSaiyan が状態を変える流れ

この図が示していること

この図では、3つの setSaiyan が、goku オブジェクトの状態をどのように変えるかを示しています。

setSaiyan(10000, 20.5) では、power と kiEnergy の両方を設定します。
setSaiyan(12000) では、power だけを変更し、kiEnergy は前の値のままです。
setSaiyan(30.5) では、kiEnergy だけを変更し、power は前の値のままです。

ここから分かるのは、同じメソッド名でも、引数の型や個数によって実行される処理が変わるということです。

多態性という考え方にもつながる

オーバーロードは、同じ名前が状況によって別の働きを持つという点で、多態性という考え方にもつながります。

ここでは難しく広げすぎなくて大丈夫です。

まずは、次の感覚を持てれば十分です。

同じ setSaiyan という名前でも、渡す引数によって選ばれる処理が変わる。

ドラゴンボールでたとえると、同じ技名でも、込める力や型の違いによって発動内容が変わるようなものです。

この柔軟さによって、メソッド名を整理しながら、いくつもの使い方を用意できます。

オーバーロードには大事なルールがある

オーバーロードには、必ず押さえておきたいルールがあります。

同じ名前のメソッドを複数定義するには、引数の型または個数が異なっている必要があります。

違いオーバーロードできるか
引数の型が違うできるint と double
引数の個数が違うできる1個と2個
引数の順番が違うできる場合があるint, double と double, int
戻り値だけが違うできないint と void の違いだけ

Sample3.java の setSaiyan は、この条件を満たしています。

メソッド違い
setSaiyan(int p)int 1個
setSaiyan(double e)double 1個
setSaiyan(int p, double e)int と double の2個

だからJavaは、呼び出し時にどれを選べばよいか判断できます。

戻り値だけが違ってもオーバーロードにはならない

ここはとても重要です。

オーバーロードでは、戻り値の違いだけではメソッドを区別できません。

たとえば、次の2つがあったとします。

int setSaiyan(int p)

void setSaiyan(int p)

この2つは、メソッド名が同じです。
そして、引数も int 1個で同じです。

違うのは戻り値だけです。

しかし、これではオーバーロードにはなりません。

なぜなら、呼び出し側が次のように書いたとき、Javaがどちらを呼べばよいか判断できないからです。

goku.setSaiyan(12000);

この呼び出しだけを見ると、引数は int 1個です。
戻り値を使うかどうかだけでは、どちらのメソッドを選ぶべきか安全に判断できません。

そのため、Javaでは戻り値だけが違うメソッドをオーバーロードとして扱いません。

比較する違いオーバーロードできるか
setSaiyan(int p) と setSaiyan(double e)できる
setSaiyan(int p) と setSaiyan(int p, double e)できる
int setSaiyan(int p) と void setSaiyan(int p)できない

オーバーロードでは、戻り値ではなく、引数を見る。
ここをしっかり押さえておくと、設計で迷いにくくなります。

図:オーバーロードできる違い、できない違い

この図が示していること

この図では、オーバーロードとして成り立つ違いと、成り立たない違いを比較しています。

引数の型が違う場合や、引数の個数が違う場合は、Javaがどのメソッドを呼ぶべきか判断できます。
そのため、オーバーロードとして定義できます。

一方で、戻り値だけが違う場合は、呼び出し時の形が同じになってしまいます。
そのため、Javaはどちらを呼べばよいか判断できず、オーバーロードとして扱えません。

ここから分かるのは、オーバーロードでは「戻り値」ではなく「引数の型や個数」が重要だということです。

オーバーロードを使うときの見方

オーバーロードを使うときは、次のように考えると整理しやすいです。

見るポイント意識すること
メソッド名同じ目的の処理を同じ名前にまとめられるか
引数の型int、double、String などで区別できるか
引数の個数1個、2個などで区別できるか
使う側の分かりやすさメソッド名が増えすぎていないか
戻り値戻り値だけで区別しようとしていないか

オーバーロードは、ただ同じ名前を使うためのテクニックではありません。

似た目的の処理を整理し、使いやすいクラスにするための考え方です。

ドラゴンボールでたとえると、同じ流派の技を、型や込める力によって使い分けるようなものです。

技名の軸がそろっているので、使う側は覚えやすくなります。
一方で、引数の違いによって、必要な動きだけを選べます。

いちばん大事な感覚

メソッドのオーバーロードで大切なのは、次の感覚です。

ポイント内容
オーバーロード同じ名前のメソッドを複数定義すること
条件引数の型または個数が違うこと
呼び分けJavaが渡された引数を見て選ぶ
便利な点似た目的の処理を同じ名前にまとめられる
注意点戻り値だけが違ってもオーバーロードできない

ドラゴンボールでたとえると、setSaiyan は「戦士の状態を整える技」です。

戦闘力だけ整える型。
気の量だけ整える型。
戦闘力と気の量をまとめて整える型。

このように、同じ技名を使いながら、渡す情報によって働きを変えられるのがオーバーロードです。

この感覚がつかめると、メソッド名をむやみに増やさず、自然で分かりやすいクラス設計ができるようになります。