オーバーロード解決
このレッスンでは、同じ名前のメソッドが複数あるときに、どの署名が呼ばれるのかを特定できるようになります。
まず予測してください
static String pick(long x) { return "long"; }
static String pick(Integer x) { return "Integer"; }
static String pick(Object x) { return "Object"; }
static String pick(int... x) { return "varargs"; }
int n = 5;
System.out.println(pick(n));候補は4つあります。どれが呼ばれるでしょうか。答えは long です。
コンパイラは3段階で候補をふるいにかける
オーバーロードの解決は実行時ではなくコンパイル時に決まります。しかも一度に全候補を比べるのではなく、次の順で段階的に探します。
- 第1段階 — ボクシングも可変長引数も使わずに呼べる候補だけを見る。ここで見つかれば確定
- 第2段階 — 第1段階で見つからなければ、ボクシングとアンボクシングを許して探す
- 第3段階 — それでも見つからなければ、可変長引数を許して探す
先ほどの pick(n) では、第1段階で int から long への拡大変換だけで呼べる pick(long) が見つかります。だから pick(Integer) も pick(int...) も検討されません。拡大変換はボクシングより強く、ボクシングは可変長引数より強い、と覚えてください。
段階ごとの結果を並べる
同じ4つの候補に、いろいろな引数を渡した結果です。
| 呼び出し | 選ばれる署名 | 理由 |
|---|---|---|
pick(5) | pick(long) | 第1段階の拡大変換で決着 |
pick((short) 5) | pick(long) | short も long へ拡大できる |
pick(Integer.valueOf(5)) | pick(Integer) | 完全一致が最優先 |
pick(5, 5) | pick(int...) | 引数2個で呼べるのはこれだけ |
3行目に注意してください。Integer は Object へも渡せるので第1段階で2つの候補が残りますが、そのときはより限定的な型が勝ちます。Integer は Object の一種なので Integer のほうが限定的です。
同点になるとコンパイルエラー
f(int, long) と f(long, int) の2つに f(5, 5) を渡すと、どちらも同じ強さで決着しません。この場合は「あいまいな参照」としてコンパイルエラーになります。試験ではこの形も出るので、候補が横並びになっていないかを確かめる癖をつけてください。
演習で確かめます
4種類の引数で pick を呼び、選ばれた署名の名前を順につないで返します。予想を紙に書いてから実行してください。
要件
- resolve は pick を 4 回呼び、その戻り値をカンマ区切りでつないで返すこと
- 呼ぶ順は int, short, Integer, 引数2個 の順にすること
- pick の 4 つの定義は書き換えないこと
入出力例
resolve(5) → "long,long,Integer,varargs"
resolve(100) → "long,long,Integer,varargs"
resolve(0) → "long,long,Integer,varargs"ヒント
編集 LuaGate編集部