Java入門|同じ名前で使い分けるメソッドのしくみ

同じ技名でも状況に合わせて使い分ける。オーバーロードがわかるとメソッド設計はもっと自然になる

クラスを使ってプログラムを書いていくと、似たような処理を何種類か用意したくなる場面が出てきます。たとえば、戦士の名前だけを設定したいこともあれば、戦闘力だけを設定したいこともありますし、名前と戦闘力をまとめて設定したいこともありますよね。こういうときに、毎回まったく別のメソッド名を考えていくと、使う側は少し覚えにくくなってしまいます。

Javaでは、そんな場面にぴったり合うしくみとして、同じ名前のメソッドを引数の違いで使い分けることができます。これがメソッドのオーバーロードです。ドラゴンボールでたとえるなら、「戦士を設定する」という同じ目的の技を、状況に応じて細かく使い分ける感覚です。名前だけを整えるときも、戦闘力だけを整えるときも、両方をまとめて整えるときも、同じ技名を使いながら、中身の働きだけを変えられるわけです。ここでは、この便利なしくみをドラゴンボールの世界に置きかえながら、やさしく丁寧に整理していきます。

オーバーロードとは何か

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

ただし、何でも同じ名前にしてよいわけではありません。
大事な条件があります。それは、引数の型や個数が異なっていることです。

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

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

この3つは、どれも setSaiyan という同じ名前ですが、

  • 引数が int 1個
  • 引数が double 1個
  • 引数が 2個

という違いがあります。

つまりJavaは、呼び出し方を見て
「今回はどの setSaiyan を呼ぶべきか」
を判断できるわけです。

ドラゴンボールで考えるオーバーロードの感覚

ドラゴンボールで考えると、このしくみはかなり自然です。

たとえば、戦士の状態を整える「setSaiyan」という技があるとします。
でも実際には、状況によってやりたいことは少しずつ違います。

  • 戦闘力だけを変えたい
  • 気の量だけを変えたい
  • 戦闘力と気の量をまとめて変えたい

このとき、全部に別々の技名をつけるのではなく、
どれも「戦士を設定する」という同じ意味の技としてまとめる
ほうが感覚的にわかりやすいです。

そのかわり、渡す情報の形を変えることで、

  • int を1つ渡したら戦闘力を設定する
  • double を1つ渡したら気の量を設定する
  • int と double を2つ渡したら両方を設定する

というふうに働きを分けます。

これがオーバーロードのイメージです。

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

Javaでは、メソッド名だけでなく、引数の型や個数も含めてメソッドを見分けます。
だから、同じ名前でも引数が違えば別のメソッドとして定義できます。

この感覚を表で整理すると、こうなります。

メソッド名引数意味
setSaiyanint戦闘力だけ設定する
setSaiyandouble気の量だけ設定する
setSaiyanint, double戦闘力と気の量をまとめて設定する

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

使う側から見ると、
「戦士の設定をしたいときは setSaiyan を使えばよい」
と覚えておけばよいので、とてもわかりやすくなります。

実際のプログラムで見てみる

では、ドラゴンボールのたとえに置きかえたサンプルプログラムを見ていきましょう。

ファイル名:Sample3.java

class Saiyan
{
    private int power;
    private double energy;

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

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

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

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

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

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

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

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

このコードでは、Saiyan クラスの中に setSaiyan という同じ名前のメソッドが3つあります。
これがまさにオーバーロードです。

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

main メソッドの最初では、こう書かれています。

goku.setSaiyan(9000, 20.5);

ここでは引数が2個あります。

  • 9000 は int
  • 20.5 は double

なので、Javaはこの呼び出しに対応する

public void setSaiyan(int p, double e)

を選びます。

その結果、

  • power に 9000
  • energy に 20.5

が入ります。

そして show() を呼び出すと、

戦闘力は9000です。
気の量は20.5です。

のように表示されます。

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

次にこう書かれています。

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

ここで渡しているのは int 型の 9500 だけです。
だからJavaは、

public void setSaiyan(int p)

を呼び出します。

このメソッドは戦闘力だけを変更するので、

  • power は 9500 に変わる
  • energy はそのまま 20.5

という状態になります。

ここがオーバーロードの便利なところです。
同じ setSaiyan という名前を使っているのに、引数が1個か2個かで、ちゃんと別の処理が選ばれます。

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

さらに次はこうです。

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

ここでは double 型の 30.5 を1つだけ渡しています。
そのためJavaは、

public void setSaiyan(double e)

を選びます。

このメソッドは気の量だけを変えるので、

  • power は 9500 のまま
  • energy は 30.5 に変わる

という流れになります。

このように、同じメソッド名でも、

  • int なら戦闘力変更
  • double なら気の量変更
  • int と double の2個なら両方変更

と使い分けられるのです。

実行結果を見ると使い分けがよくわかる

このプログラムの実行結果は、次のようになります。

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

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

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

この結果を見ると、同じ setSaiyan という名前を使っているのに、場面ごとにちょうどよい処理が実行されていることがわかります。

これが、オーバーロードの大きなメリットです。

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

オーバーロードのよさは、使う側の視点で考えるとさらによくわかります。

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

  • setPower
  • setEnergy
  • setPowerEnergy

もちろん、これでも動きます。
でも、似た目的の処理なのにメソッド名がばらばらになると、使う側は少し覚えにくくなります。

一方でオーバーロードを使えば、
「戦士の設定をするときは setSaiyan を使う」
と覚えておけば十分です。

そのうえで、渡す引数の形によって自動的に適切な処理が選ばれます。

表で比べると、こんなイメージです。

方式覚える名前
別名で分けるsetPower、setEnergy、setPowerEnergy
オーバーロードを使うsetSaiyan だけ

この「覚える名前が整理される」という点は、クラスを部品として使うときにとても便利です。

これは「似た処理を1つの名前にまとめる」考え方

オーバーロードは、ただ同じ名前を使えるというだけではありません。
本質は、似た役割を持つ処理を、ひとつの名前にまとめて設計できることです。

今回の setSaiyan は、どれも「戦士の状態を設定する」という共通テーマを持っています。

  • 戦闘力だけ設定する
  • 気の量だけ設定する
  • 両方を設定する

という違いはあっても、全部「戦士の設定」という同じ仲間の処理です。

だから、同じ名前でまとめると自然です。
オーバーロードは、この自然さをコードの形にできる仕組みだと言えます。

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

本文でも出てくるように、1つの名前が状況に応じて別々の働きを持つことは、多態性という考え方にもつながります。

ここでは難しく広げすぎなくて大丈夫ですが、少なくとも

同じ setSaiyan という名前でも、渡す引数によって別の処理になる

という現象は、Javaらしい大事な特徴です。

ドラゴンボールで言えば、「同じ技名を使っているのに、込める情報によって発動内容が変わる」ような感覚です。
この柔軟さが、クラスの使いやすさを高めてくれます。

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

ここで必ず押さえておきたいのが、オーバーロードには条件があることです。

引数の型または個数が異なっていなければなりません。

つまり、次のような違いが必要です。

  • int と double のように型が違う
  • 引数1個と引数2個のように個数が違う

今回の setSaiyan はこの条件を満たしています。

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

だからJavaは、どれを呼ぶべきか判断できます。

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

ここはとても大事な注意点です。

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

int setSaiyan(int p)
void setSaiyan(int p)

この2つは、メソッド名は同じで、引数もまったく同じです。
違うのは戻り値だけです。

でも、これではオーバーロードできません。

なぜかというと、呼び出す側が

setSaiyan(9000);

と書いたときに、どちらを呼ぶべきか判断できないからです。

つまりJavaは、戻り値の違いではなく、引数の違いでメソッドを見分けるということです。

ここはとても重要なので、表でも整理しておきます。

違いオーバーロードできるか
引数の型が違うできる
引数の個数が違うできる
戻り値だけが違うできない

このルールを知っておくと、オーバーロードの設計で迷いにくくなります。

図で見るとオーバーロードの流れがわかりやすい

この図では、左側に setSaiyan という共通の名前があり、そこから3種類のメソッドに枝分かれしています。
これによって、「名前は同じでも、中身は引数によって分かれている」ということがひと目でわかります。

右側には実際の呼び出し例が並んでいて、それぞれが対応するメソッドへつながっています。
たとえば goku.setSaiyan(9500) は int 型1個なので setSaiyan(int p) に進み、goku.setSaiyan(30.5) は double 型1個なので setSaiyan(double e) に進みます。

また右下の注意枠を見ることで、戻り値だけ違ってもオーバーロードにはならない、という大事なルールも整理しやすくなります。

いちばん大事な感覚

「同じ名前で使い分けるメソッドのしくみ」で大切なのは、次の感覚です。

  • 同じクラスの中で、同じ名前のメソッドを複数定義できる
  • ただし、引数の型または個数が異なっていなければならない
  • 呼び出し時には、渡した引数に応じて適切なメソッドが自動的に選ばれる
  • 似た役割の処理を同じ名前でまとめられるので、使う側にとってわかりやすい
  • 戻り値だけが違うメソッドではオーバーロードできない

ドラゴンボールで言いかえるなら、

  • 戦士を設定するという同じ目的の技を
  • 戦闘力だけ変更する形
  • 気の量だけ変更する形
  • 両方まとめて変更する形

として使い分けられる、ということです。

この感覚がつかめると、メソッド設計がかなり自然に見えてきます。
オーバーロードは、クラスを使いやすく、直感的にしてくれる大切な仕組みです。