概要
メソッドのオーバーロード時、特に引数の数が同じ場合、どのメソッドが優先して呼び出されるのかをまとめる。
※初学者が書いた勉強メモであることを念頭にお読み下さい。誤りや記載不足の項目がありましたら、コメントにてご指摘いただけますと幸いです。
本編
基本型
public class Main {
public static void main(String[] args) {
int a = 2;
int b = 3;
method(a, b);
}
//➀
public static void method(int a, int b) {
System.out.println("完全一致");
}
//➁
public static void method(long a, long b) {
System.out.println("基本型の拡大変換");
}
//➂
public static void method(Integer a, Integer b) {
System.out.println("オートボクシング");
}
//➃
public static void method(int... num) {
System.out.println("可変長引数");
}
}
methodメソッドの実行優先順位は
➀. 実引数の型が、仮引数の型と完全に一致している場合
➁. 実引数の型から仮引数の型へ、基本型の拡大変換ができる場合
➂. 実引数の型から仮引数の型へ、オートボクシングできる場合
➃. 仮引数が、実引数の型の値を可変個受け取れる(可変長引数である)場合
である。
以下の場合は➁Aが優先される。
//➁A
public static void method(long a, long b) {
System.out.println("基本型の拡大変換(整数型→整数型)");
}
//➁B
public static void method(double a, double b) {
System.out.println("基本型の拡大変換(整数型→浮動小数点型)");
}
コンパイルエラーとなる例
以下のメソッド2つを両方書くとエラーとなる。
public static void method(long a, int b) {
System.out.println("基本型の拡大変換 その1");
}
public static void method(int a, long b) {
System.out.println("基本型の拡大変換 その2");
}
結果
エラー: methodの参照はあいまいです
method(a, b);
^
Mainのメソッド method(long,int)とMainのメソッド method(int,long)の両方が一致します
上記の例では、両方のメソッドが「➁. 実引数の型から仮引数の型へ、基本型の拡大変換ができる場合」に該当する。
同じ優先順位のメソッドが複数あると、どのメソッドを実行すればいいのか定まらないためエラーが出る。
参照型
public class Main {
public static void main(String[] args) {
Integer a = 2;
Integer b = 3;
method(a, b);
}
//➀
public static void method(Integer a, Integer b) {
System.out.println("完全一致");
}
//➁
public static void method(Number a, Number b) {
System.out.println("スーパークラス");
}
//➂
public static void method(int a, int b) {
System.out.println("アンボクシング");
}
//➃
public static void method(Integer... num) {
System.out.println("可変長引数");
}
}
methodメソッドの実行優先順位は
➀. 実引数の型が、仮引数の型と完全に一致している場合
➁. 仮引数の型が、実引数の型のスーパークラス型である場合
➂. 実引数の型から仮引数の型へ、アンボクシングできる場合
➃. 仮引数が、実引数の型の値を可変個受け取れる(可変長引数である)場合
である。
以下の場合は➁Aが優先される。
//➁A
public static void method(Number a, Number b) {
System.out.println("スーパークラス");
}
//➁B
public static void method(Object a, Object b) {
System.out.println("スーパークラスのスーパークラス");
}
余談
参照型→基本型と基本型→参照型
以下のコードはエラーにならない。
public class Main {
public static void main(String[] args) {
Integer a = 2;
Integer b = 3;
method(a, b);
}
public static void method(double a, double b) {
System.out.println("Integerからdouble");
}
}
結果
Integerからdouble
このメソッドの優先順位は、➂(アンボクシング)と➃(可変長引数)の間となる。
メソッド呼び出しの時にアンボクシング(Integerからint)→基本型の拡大変換(intからdouble)という二段階の手順を踏むため、ただアンボクシングをする時に比べて優先順位が下がるのであろう。
しかし、以下のコードはエラーになる。
public class Main {
public static void main(String[] args) {
int a = 2;
int b = 3;
method(a, b);
}
public static void method(Double a, Double b) {
System.out.println("intからDouble");
}
}
結果
エラー: 不適合な型: intをDoubleに変換できません:
method(a, b);
^
何故前者はエラーにならず、後者はエラーになるのか
javaの言語仕様が記載された上記リンク内「5.3. Invocation Contexts」に、このような記述がある。
Strict invocation contexts allow the use of one of the following:
・an identity conversion (§5.1.1)
・a widening primitive conversion (§5.1.2)
・a widening reference conversion (§5.1.5)
Loose invocation contexts allow a more permissive set of conversions, because they are only used for a particular invocation if no applicable declaration can be found using strict invocation contexts. Loose invocation contexts allow the use of one of the following:
(中略)
・a boxing conversion (§5.1.7)
(中略)
・an unboxing conversion (§5.1.8)
・an unboxing conversion followed by a widening primitive conversion
簡単にまとめると、「実引数の型が、仮引数の型と完全に一致している」「実引数の型から仮引数の型へ、拡大変換できる」といった厳密なメソッドが呼び出せない時のみ、より緩い条件でのメソッド呼び出しができる。本編で挙げた「➂. 実引数の型から仮引数の型へ、オートボクシング/アンボクシングできる場合」は、緩い条件でのメソッド呼び出しに含まれる。
そして先ほど述べた、「メソッド呼び出しの時にアンボクシング→基本型の拡大変換という二段階の手順を踏む場合」も、緩い条件の一種として仕様上許可されている。
逆に、「基本型の拡大変換→オートボクシングという二段階の手順を踏む場合」はリンク先に記載がない(=仕様上許可されていない)。つまり、実引数の型intをdoubleに、doubleをDoubleに、自動で変換してメソッドを呼び出すことはできないのである。
ちなみに、「オートボクシング→参照型の拡大変換」も仕様上許可されている。つまり、以下のコードはエラーにならない。無論、優先順位は本編➂(オートボクシング)と➃(可変長引数)の間である。
public class Main {
public static void main(String[] args) {
int a = 2;
int b = 3;
method(a, b);
}
public static void method(Number a, Number b) {
System.out.println("intからNumber");
}
}