第5章 オーバーロード
5.1 オーバーロードについて
これまでクラス内にはフィールド変数、メソッド、コンストラクタといったメンバを定義できることを学習してきました。その中でメソッドとコンストラクタは、オーバーロード(多重定義)という機能により、同じ名前で複数定義できる仕組みが備わっています。
その仕組みについて次の項より説明をしていきます。
5.1.1 メソッドのオーバーロードの仕組み
オーバーロードとは簡単に言うと同じ名前でメソッドを定義することです。但しメソッドのオーバーロードを利用するには、以下のようなルールがあります。
- 同じ名前でメソッドを定義する。
- 各メソッドの引数の型・個数・並びが異なるようにする。
メソッドをオーバーロードする理由
メソッドをオーバーロードすることによってインターフェースに一貫性を持たせ、どのデータ型を渡す場合でも、論理的に整合性のある形でメソッドを呼び出すことができます。新しい名前や名前規約を考え出すよりも、同じ名前を使用した方が、メソッドがどのような機能を持っているか思い出しやすくなります。
書式:メソッドのオーバーロード
メソッドのオーバーロードは、同じメソッド名をつけ、引数の型、個数、並びを変えます
上記2つの例は一見定義する分には問題ないように思えますが、定義するだけではなく「呼び出し元で見分けがつかない」点が問題になっています。
①のケースが駄目な理由はメソッドを呼び出す時に、戻り値の型が違うだけで同じ引数の型同士ではどちらのメソッドを呼び出していいのか見分けがつきません。戻り値の型はメソッドが呼ばれた後の情報になるため、呼び出し元が見分けるときの情報にはなりません。
②のケースが駄目な理由はこれも①と同じように、仮引数の名前が違うだけで同じ引数の型、個数同士ではどちらのメソッドを呼び出していいのか見分けがつきません。引数の名前は呼び出した後に使われる情報なため、これも呼び出し元が見分けるときの情報にはなりません。
図 5.1.1: オーバーロードの正しい例と間違い例限
メソッドを見分けるシグネチャについてメソッドの「名前」と「引数の組み合わせ」のことをメソッドの「シグネチャ」といいます。Javaではこのシグネチャによりメソッドを見分けています。引数の組み合わせとは引数の型・個数・並びのことを意味します。戻り値の型や、引数の名前は関係ないことに注意が必要です。
それでは、次の項にて実際にメソッドのオーバーロード機能を利用したサンプルを紹介します。
5.1.2 メソッドのオーバーロードを使用したプログラム
同じ名前のメソッドをオーバーロード機能で定義し、そのメソッドを各々呼び出して動作することを確認します。
① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch05
③ 名前: OverLoadMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch05; class Computer1{ private String os; private int memory; //オーバーロードメソッド1 public void setComputer(String os){ this.os = os; System.out.println("OSを「" + os + "」に変更しました。"); } //オーバーロードメソッド2 public void setComputer(int memory){ this.memory = memory; System.out.println("メモリサイズを「" + memory + "MByte」に変更しました。"); } //オーバーロードメソッド3 public void setComputer(String os,int memory){ this.os = os; this.memory = memory; System.out.println("OS「" + os + "」メモリサイズ「" + memory + "MByte」に変更しました。"); } public void show(){ System.out.println("パソコンのOSは「" + os + "」です。"); System.out.println("メモリサイズは「" + memory + "MByte」です。"); } } public class OverLoadMethod { public static void main(String[] args) { //オブジェクト生成 Computer1 com = new Computer1(); //引数2個で引数の型がString型、int型の並び順のsetComputerメソッド呼び出し com.setComputer("WindowsXP", 2048); //メソッドのオーバーロード com.show(); System.out.println("-------------------------------------------------------"); //引数String型のsetComputerメソッド呼び出し com.setComputer("Windows2000"); //メソッドのオーバーロード com.show(); System.out.println("-------------------------------------------------------"); //引数int型のsetComputerメソッド呼び出し com.setComputer(512); //メソッドのオーバーロード com.show(); } }実行結果
図 5.1.2: メソッドのオーバーロード仕組み
今回のソースコード内では3種類のsetComputerメソッドを呼び出しています。
①「引数String、int型2つのもの」、②「引数String型1つのもの」、③「引数int型1つのもの」とsetComputerメソッドが、それぞれ正しく呼び出されています。
このオーバーロード機能は、「似たような複数の処理をオーバーロードしておくことで、1つのメソッド名を覚えて使うだけで、自動的に引数の型・個数・並びに応じた処理が行われること」になります。
オーバーロード機能を利用したクラスの設計を行っておけば分かりやすく、利用しやすいコードが記述でき大変便利になります。
解説
3~27行目のComputer1クラス内のオーバーロード機能で定義したsetComputer()メソッド一覧は以下の表になります。
上記の表から確認できるように、引数の型や個数が全て異なっておりオーバーロード機能の制約に従っていることが分かります。
32行目でComputer1クラスよりnew演算子を用いてオブジェクト生成しています。今回はコンストラクタを定義していないため、デフォルトコンストラクタが動作しています。
Computer1 com = new Computer1();
35行目で引数がString型とint型の2つのsetComputerメソッドを呼び出し、引数の値「WindowsXP、2048」を各フィールド変数へ設定しています。36行目のshowメソッドを利用して値が正しく設定されたか確認しています。
出力メッセージからComputer1クラス内の引数が2つのsetComputerが、正しく呼び出されているのが確認できます。
40行目で引数がString型1つのsetComputerメソッドを呼び出して、引数の値「Windows2000」をフィールド変数osへ設定しています。41行目でshowメソッドを利用して値が正しく設定されたか確認しています
出力メッセージからComputer1クラス内の引数がString型1つのsetComputerが、正しく呼び出されているのが確認できます。
45行目で引数がint型1つのsetComputerメソッドを呼び出して、引数の値「512」をフィールド変数memoryへ設定しています。46行目でshowメソッドを利用して値が正しく設定されたか確認しています。
出力メッセージからComputer1クラス内の引数がint型1つのsetComputerが、正しく呼び出されているのが確認できます。
このように、オーバーロード機能を利用することで、似たような処理を行うメソッドを同じ名前で定義し、大変利用しやすいメソッドにすることが可能です。
5.1.3 メソッドのオーバーロードができないプログラム
オーバーロードの制約に従っていないメソッド定義を実際に行いコンパイルエラーになることを確認します。
① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch05
③ 名前: OverLoadMethodError
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch05; class Computer2 { private String os; private int memory; // オーバーロードメソッド1 public void setComputer(String os) { this.os = os; } // オーバーロードメソッド2 public String setComputer(String os) { String oldOs = this.os; this.os = os; return oldOs; } // オーバーロードメソッド3 public void setComputer(String os1, int memory1) { this.os = os1; this.memory = memory1; } // オーバーロードメソッド4 public void setComputer(String os2, int memory2) { this.os = os2; this.memory = memory2; } } public class OverLoadMethodError { public static void main(String[] args) { // オブジェクト生成 Computer2 com = new Computer2(); com.setComputer("WindowsXP"); com.setComputer("WindowsXP", 1024); } }
実行結果
オーバーロードのルールに従っていない為、コンパイルエラーになり実行できません。
Eclipse上でのソースファイル結果
解説
8~10行のメソッドと12~16行のメソッドは引数の型・個数が同じになっており、オーバーロードの制約に従っていません。戻り値の型が違うだけでは見分けがつかないため、メソッドが定義できないことが確認できます。
public void setComputer(String os){ this.os = os; } public String setComputer(String os){ String oldOs = this.os; this.os = os; return oldOs; }
図 5.1.3: メソッドのオーバーロードができないケース(戻り値の型が異なるだけ)
18~21行のメソッドと23~26行の引数の名前は異なっているが、メソッドは引数の型・個数が同じになっており、オーバーロードの制約に従っていません。引数の名前が違うだけでは見分けがつかないため、メソッドが定義できないことが確認できます。
public void setComputer(String os1,int memory1){ this.os = os1; this.memory = memory1; } public void setComputer(String os2,int memory2){ this.os = os2; this.memory = memory2; }
図 5.1.4: メソッドのオーバーロードができないケース(引数の名前が異なるだけ)
5.1.4 コンストラクタのオーバーロードの仕組み
これまでメソッドのオーバーロードについて学習してきました。メソッドによく似たコンストラクタも、引数の型・個数・並びが異なっていれば、オーバーロード機能を利用して複数定義することができます。
コンストラクタのオーバーロードもメソッドと同じルールがあり、それさえ守れば使い方は同じになります。
- 同じ名前でコンストラクタを定義する。
- 各コンストラクタの引数の型・個数・並びが異なるようにする。
※引数の名前はコンストラクタのオーバーロードには関係ありません。
コンストラクタをオーバーロードする理由
前章でコンストラクタを定義するパターンとして①デフォルトコンストラクタ、②引数なしコンストラクタ、③引数有りコンストラクタが定義できることを学習しました。その際に引数ありのコンストラクタだけ定義している場合、引数なしのコンストラクタ呼び出しが行えなくなる不便なケースがありました。このオーバーロード機能を利用すれば、引数なしと引数ありのコンストラクタを両方定義でき、引数なしかありでオブジェクトの生成を状況に応じて使い分けることが可能になります。
では実際に次の項で、コンストラクタをオーバーロードしたプログラムを紹介していきます。
5.1.5 コンストラクタのオーバーロードサンプル
コンストラクタを複数定義して動作させるプログラムです。
同じ名前のコンストラクタをオーバーロード機能で定義し動作することを確認します。
① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch05
③ 名前: OverLoadConstructor
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch05; class Computer3{ private String os; private int memory; // 引数なしコンストラクタ public Computer3(){ this.os = null; this.memory = 0; } // 引数2つもつコンストラクタ public Computer3(String os, int memory){ this.os = os; this.memory = memory; System.out.println("OS「" + os + "」メモリサイズ「" + memory + "MByte」のパソコンを作成しました。"); } public void show(){ System.out.println("パソコンのOSは「" + os + "」です。"); System.out.println("メモリサイズは「" + memory + "MByte」です。"); } } public class OverLoadConstructor { public static void main(String[] args) { // オブジェクト生成1つ目 Computer3 com1 = new Computer3(); // オーバーロード引数なしコンストラクタ com1.show(); System.out.println("-----------------------------------------------------------------"); // オブジェクト生成2つ目 Computer3 com2 = new Computer3("WindowsXP",2048); // オーバーロード引数ありコンストラクタ com2.show(); } }実行結果
オーバーロードしたメソッドが呼び分けられる仕組み
前章で引数ありのコンストラクタのみを定義してしまった場合、デフォルトコンストラクタが呼び出せなくなり、引数なしでのオブジェクト生成ができなくなりました。このオーバーロード機能を用いて引数なし、ありのコンストラクタを用意しておけば、どちらでもオブジェクト生成が可能になります。
解説
3~22行目のComputer3クラス内のオーバーロード機能で定義したコンストラクタ一覧は以下の表になります。
上記の表から確認できるように、引数の型や個数が異なっており、メソッドで定義したようにコンストラクタもオーバーロード機能の制約に従っていることが分かります。
26行目でのオブジェクト生成1回目は、引数なしのコンストラクタで行っています。27行目のshowメソッドでフィールド変数の値を画面に出力しています。osにはnull、memoryには0が設定されており、引数なしのコンストラクタが呼び出されているのが確認できます。
30行目でのオブジェクト生成2回目は、引数2つのコンストラクタで行っています。31行目のshowメソッドでフィールド変数の値を画面に出力しています。osにはWindowsXP、memoryには2048が設定されており、引数2つのコンストラクタが呼び出されているのが確認できます。