1. メソッドのオーバーロード
メソッドのオーバーロード(method overloading)とは、同じクラス内で同名だがパラメータリスト(型・個数・順序)が異なる複数のメソッドを宣言できる仕組みです。呼び出し時には、渡した引数に応じてコンパイラが自動的に最適なバージョンを選択します。
身近な例え
銀行のサポートに電話をかけることを想像してください。個人向け、法人向け、VIP 向けなど、複数の番号があります。番号は異なっても目的は同じで、助けを得ることです。プログラミングにおけるメソッドのオーバーロードは、1 つのサポート番号に電話すると、問い合わせ内容(渡したパラメータ)に応じて自動的に適切な担当者につながるようなものです。
Java におけるオーバーロードの構文
Java ではとても簡単で、同じ名前でパラメータが異なるメソッドを複数宣言します。
重要なルール:
オーバーロードの区別はパラメータリスト(型・個数・順序)だけで行われます。
戻り値の型だけの違いによるオーバーロードは不可です!
同名かつ同一パラメータのメソッドは、たとえ戻り値の型が異なってもコンパイラは区別できません。
簡単な例
public class Printer {
// 整数の出力
void print(int x) {
System.out.println("int: " + x);
}
// 文字列の出力
void print(String s) {
System.out.println("String: " + s);
}
// 2 つの数の出力
void print(int x, int y) {
System.out.println("int, int: " + x + ", " + y);
}
}
使用例:
Printer printer = new Printer();
printer.print(42); // print(int x) が呼ばれる
printer.print("Hello!"); // print(String s) が呼ばれる
printer.print(5, 10); // print(int x, int y) が呼ばれる
コンパイラは、渡された引数に基づいてどのメソッドを呼び出すかを自動的に判断します。
2. コンパイラはどのメソッドを選ぶか
オーバーロードされたメソッドを呼び出すと、コンパイラは引数の一覧を解析し、最も適したバージョンを探します。
選択の基準:
- 引数の個数が一致していること。
- 各引数の型が一致していること(あるいは、int → double のように変換可能であること)。
- 複数の候補がある場合は、最も特化したものが選ばれる。
例:
public class OverloadDemo {
void show(int x) {
System.out.println("show(int): " + x);
}
void show(double x) {
System.out.println("show(double): " + x);
}
public static void main(String[] args) {
OverloadDemo demo = new OverloadDemo();
demo.show(5); // show(int): 5
demo.show(5.5); // show(double): 5.5
}
}
demo.show(5) を呼ぶと、コンパイラは show(int) を選びます。5.5 で呼ぶと、show(double) が選択されます。
型変換
適切なメソッドが見つからない場合、コンパイラは引数の型変換(たとえば int → double)を試みます。ただし、それが可能であり、かつ曖昧でない場合に限ります。
void print(double x) { /* ... */ }
print(5); // int 5 は double 5.0 に変換される
戻り値の型だけの違いによるオーバーロードは機能しない!
多くの初心者が次のように書こうとします:
// エラー!このようなオーバーロードはできません
int sum(int x, int y) { return x + y; }
double sum(int x, int y) { return (double) (x + y); }
sum(2, 3) とだけ書いても、どのメソッドを呼びたいのかコンパイラには分かりません。
覚えておきましょう: オーバーロードはパラメータによってのみ成立し、戻り値の型だけでは成立しません。
3. コンストラクタのオーバーロード
通常のメソッドだけでなく、コンストラクタもオーバーロードできます。
public class Person {
String name;
int age;
// 名前だけのコンストラクタ
public Person(String name) {
this.name = name;
this.age = 0; // 既定値
}
// 名前と年齢のコンストラクタ
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
使用例:
Person p1 = new Person("アンナ");
Person p2 = new Person("ボリス", 25);
4. 実用的な例
オーバーロードを持つ Calculator
Calculator クラスを作って、異なる型や個数の数値を加算できるようにしてみましょう。
public class Calculator {
// 2 つの整数の加算
int add(int a, int b) {
return a + b;
}
// 3 つの整数の加算
int add(int a, int b, int c) {
return a + b + c;
}
// 2 つの double の加算
double add(double a, double b) {
return a + b;
}
}
使用例:
Calculator calc = new Calculator();
System.out.println(calc.add(2, 3)); // 5
System.out.println(calc.add(1, 2, 3)); // 6
System.out.println(calc.add(2.5, 3.1)); // 5.6
標準ライブラリにおけるメソッドのオーバーロード
気付いていなくても、すでにオーバーロードを使ったことがあるはずです。たとえば System.out の println は、さまざまな型に対してオーバーロードされています。
System.out.println("Hello"); // println(String)
System.out.println(123); // println(int)
System.out.println(3.14); // println(double)
System.out.println(true); // println(boolean)
PrintStream クラスのソースを開いてみると、println のオーバーロードが多数あることが分かります。
5. オーバーロードを使うとよい場面
メソッドのオーバーロードは強力ですが、賢く使う必要があります。次のような場合に便利です。
- メソッドのロジックは同じだが、引数が異なる場合。
- クラスの API をより使いやすく柔軟にしたい場合。
- メソッド呼び出しの古い形と新しい形を併存させたい場合。
実例:
学習用アプリ(例えばタスク管理ツール)では、異なるパラメータでタスクを追加するメソッドを次のように用意できます。
public class TaskManager {
void addTask(String description) { ... }
void addTask(String description, int priority) { ... }
void addTask(String description, int priority, String deadline) { ... }
}
このアプローチは利用者にとって使いやすいだけでなく、将来の変更に対してクラスを柔軟にします。
6. オーバーロードと varargs(可変長引数)
Java では ...(varargs)を使って、可変個のパラメータを受け取るメソッドを宣言できます。
void printAll(String... messages) {
for (String msg : messages) {
System.out.println(msg);
}
}
これで次のように呼び出せます:
printAll("Hello");
printAll("One", "Two", "Three");
varargs を使ったオーバーロードも可能ですが注意が必要です。固定個数のパラメータを持つメソッドと varargs のメソッドが両方ある場合、コンパイラはまず固定個数のシグネチャとの完全一致を優先して探します。
7. メソッドのオーバーロードでありがちなミス
エラー1: 引数型の取り違え。
void process(int x) と void process(double x) があるとき、process(5) は前者、process(5.0) は後者が呼ばれます。しかし process(5L) のように呼ぶと、コンパイラは最適な一致を探すため、意外なオーバーロードが選ばれたり(あるいは曖昧だと報告されたり)することがあります。
エラー2: オートボクシングを伴うオーバーロード。
void foo(Integer x) と void foo(Long x) がある場合、foo(5) はコンパイルエラーになることがあります。コンパイラはどちらを選ぶべきか判断できないからです。5 は Integer にも Long にも変換し得るとみなされるためです。
エラー3: 戻り値の型だけの違いによるオーバーロード。
前述のとおり、戻り値の型が異なるだけのメソッドはオーバーロードできません。
エラー4: オーバーロードと継承。
基底クラスにメソッドがあり、サブクラスで同名だが異なるシグネチャのメソッドを宣言した場合、それはオーバーライドではなくオーバーロードです。ここを混同しがちです。詳細は次回の講義で扱います。
GO TO FULL VERSION