16.

Java メソッドオーバーロード完全ガイド

編集
この記事の要点
  • オーバーロード (overload) = 同名メソッドを引数の または で複数定義
  • 戻り値の型だけ違うのはオーバーロード不可 (コンパイルエラー)
  • コンストラクタもオーバーロード可。this(...) で別コンストラクタ呼び出し
  • 可変長引数 (varargs) String... args は型推論で複雑化するので注意
  • オーバーライド (override) は親子クラスで同シグネチャを上書き → 別物

オーバーロードとは

同じ名前のメソッドを、引数の型や数を変えて複数定義すること。呼び出し側はコンパイル時に最適なメソッドが選択されます。

public class Printer {
    // 同名 println を引数違いで複数定義
    public void println(String s)  { System.out.println(s); }
    public void println(int n)     { System.out.println(n); }
    public void println(double d)  { System.out.println(d); }
    public void println(Object o)  { System.out.println(o); }
    public void println()          { System.out.println(); }
}

Printer p = new Printer();
p.println("hello");  // String 版
p.println(42);       // int 版
p.println(3.14);     // double 版
p.println();         // 引数なし版

オーバーロードの条件

変更点オーバーロード可?
引数のが違う✅ OK
引数のが違う✅ OK
引数の順序が違う (型違い)✅ OK (推奨はしない)
引数名だけ違う❌ NG
戻り値の型だけ違う❌ NG
throws 句だけ違う❌ NG
修飾子 (static / final 等) だけ違う❌ NG
// ❌ コンパイルエラー: 戻り値だけ違う
int    sum(int a, int b) { return a + b; }
double sum(int a, int b) { return a + b; }

// ✅ 引数の型が違うのでオーバーロード成立
int    sum(int a, int b)       { return a + b; }
double sum(double a, double b) { return a + b; }

// ✅ 引数数違い
int sum(int a, int b)        { return a + b; }
int sum(int a, int b, int c) { return a + b + c; }

コンストラクタのオーバーロード

public class User {
    private String name;
    private int age;
    private String email;

    // 全引数
    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    // ✅ this(...) で他コンストラクタへ委譲
    public User(String name, int age) {
        this(name, age, "noemail@example.com");
    }

    public User(String name) {
        this(name, 0);
    }

    public User() {
        this("Anonymous");
    }
}

new User("Taro", 25, "taro@x.com");
new User("Taro", 25);
new User("Taro");
new User();

可変長引数 (varargs)

// 「0 個以上の引数を受け取る」
public int sum(int... nums) {
    int total = 0;
    for (int n : nums) total += n;
    return total;
}

sum();              // 0
sum(1);             // 1
sum(1, 2, 3);       // 6
sum(new int[]{1, 2, 3});  // 6 (配列を直接渡せる)

// オーバーロードと varargs の優先順位
public void f(int x)        { System.out.println("int"); }
public void f(int... xs)    { System.out.println("varargs"); }

f(1);   // → "int" (より具体的なメソッドが優先)

// 可変長引数は ★ 引数リストの最後にだけ
public void log(String tag, Object... args) { ... }  // ✅
// public void bad(Object... args, String tag) { ... }  // ★ コンパイルエラー

自動型変換 (widening / boxing) と選択順

引数の型が完全一致しない場合、Java は次の順で最適なオーバーロードを探します:

  1. 完全一致
  2. widening (拡大変換 int → long → float → double)
  3. autoboxing (int ↔ Integer)
  4. varargs
public void f(long x)     { System.out.println("long"); }
public void f(Integer x)  { System.out.println("Integer"); }
public void f(Object x)   { System.out.println("Object"); }
public void f(int... xs)  { System.out.println("varargs"); }

f(10);
// 完全一致 (int) なし → widening (long) が当たる → "long"

// long を消すと…
// → Integer (autoboxing) → "Integer"

// 全部消すと…
// → varargs → "varargs"

ジェネリクスとオーバーロードの相互作用

// ❌ コンパイルエラー: 型消去後シグネチャが同じ
public void f(List<String> list) { ... }
public void f(List<Integer> list) { ... }
// → どちらも f(List) になり区別できない

// ✅ 一方をリネームするか、List 以外で区別
public void fString(List<String> list)  { ... }
public void fInteger(List<Integer> list) { ... }

オーバーロード vs オーバーライド

オーバーロードオーバーライド
意味同名で引数違いの複数メソッド親クラスのメソッドを子で再定義
クラス関係同一クラス内 (または継承先)親子クラス
シグネチャ引数違い引数 / 戻り値型 (共変まで OK) 同じ
呼び出し決定コンパイル時 (静的)実行時 (動的、ポリモーフィズム)
static メソッドオーバーロード可★ オーバーライド不可 (隠蔽)
final メソッドオーバーロード可★ オーバーライド不可
private メソッドオーバーロード可子クラスから見えない
注釈無し@Override
class Parent {
    public void greet()         { System.out.println("Hello"); }       // メソッドA
    public void greet(String n) { System.out.println("Hi " + n); }     // メソッドB (オーバーロード)
}

class Child extends Parent {
    @Override
    public void greet()          { System.out.println("こんにちは"); } // ★ オーバーライド
}

Parent p = new Child();
p.greet();        // "こんにちは"  (動的ディスパッチ)
p.greet("Taro");  // "Hi Taro"   (親の B が呼ばれる)

オーバーロード設計のベストプラクティス

  • 同じ意味の処理のみオーバーロードする (引数違いで全く違う処理は別名に)
  • 引数違いは互いに委譲する: sum(a, b)sum(a, b, 0)
  • varargs と通常メソッドを共存させると曖昧になる → 避ける
  • 同一型を順序違いで複数定義 ((int, String)(String, int)) は読み手が混乱 → 別名推奨
  • autoboxing / widening の選択ルールを意識。意図しないオーバーロードが選ばれる事故を防ぐ

FAQ

Q: println(int)println(Object) 両方あるとき println(null) はどうなる?
A: 「最も具体的な」型が選ばれる規則により、参照型同士なら最も具体的なもの。null は基本型に当てはまらないので参照型側が選ばれる。

Q: static メソッドはオーバーロードできる?
A: できる。static 同士、または static と非 static の組合せも可。ただしオーバーライドはできない (隠蔽)

Q: コンストラクタを this() 呼び出すときの注意
A: this(...)コンストラクタ本体の最初の文でなければならない。super(...) も同様。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本的なルール
  2. データ型
  3. 変数
  4. 定数
  5. 配列
  6. コレクション(List,Set,Queue)
  7. Map(連想配列)
  8. 演算子
  9. 条件分岐
  10. 繰り返し制御文
  11. クラス
  12. メソッド
  13. インスタンス化
  14. コンストラクタ
  15. staticキーワード
  16. オーバーロード
  17. 継承
  18. オーバーライド
  19. this
  20. super
  21. パッケージ
  22. アクセス修飾子
  23. 抽象クラス・メソッド
  24. インターフェース
  25. カプセル化
  26. データベース接続
  27. セッション
  28. ファイル入出力
  29. ラムダ式

最近更新/作成されたページ