Перегрузка метода, приведение типов
Есть код:
public class Main_num {
public static void num(Integer i) {
System.out.println("num(Integer)");
}
public static void num(short i) {
System.out.println("num(short)");
}
public static void num(long i) {
System.out.println("num(long)");
}
public static void num(Object i) {
System.out.println("num(Object)");
}
public static void num(int... i) {
System.out.println("num(int...)");
}
public static void main(String[] args) {
num(5);
}
Который при запуске выдает num(long)
. Остальные методы понятно почему не вызываются, но для меня не очевидно, почему программа распознала цифру 5 как long, а не short и вызвала соответствующий метод (num(long i)), не вызвав num(short i). Может кто-нибудь пояснить?
Ответы (2 шт):
Компилятор видит вызов метода для числа 5, которое, как и все числа по умолчанию в Java, имеет тип int
. Он, очевидно, влезает в long
, но не влезает в short
. Вот и вызывается версия метода, перегруженная именно для long
.
Причём можно вызвать перегрузку и для short
, если явно привести передаваемое в метод число к этому типу:
num((short)5);
Результат:
num(short)
В данном случае компилятор не может вызвать перегруженный метод, принимающий short
при передаче целочисленной константы, имеющей тип int
, так как это может вызвать потерю точности (int
в два раза "шире", чем short
).
Поскольку нет подходящего варианта метода, принимающего примитивный int
, компилятор сначала будет искать метод, принимающий аргумент следующего подходящего примитивного типа, т.е. long
.
Эта логика описана в спецификации языка JLS 15.12.2. Compile-Time Step 2: Determine Method Signature:
Если существует более одного метода, выбирается наиболее конкретный (most specific)
Метод является подходящим (applicable), если он подходит при строгом вызове (полном совпадении типов аргумента), слабом вызове (типы аргументов совпадают после авто-боксинга), или вызове переменного числа аргументов. ... Сначала определяются потенциально подходящие методы.
Затем процесс определения делится на три фазы, чтобы обеспечить обратную совместимость с версиями Java до 5.0 (когда был добавлен авто-боксинг).
В данном случае для вызова перегруженного варианта с аргументом типа short
следовало вызывать метод, передавая туда значение типа byte
или short
:
byte b = 5;
num(b); // num(short)
num((byte) 5); // num(short)
Даже при вызове метода с аргументом типа char
будет выполнено расширение типа до long
, так как беззнаковый 16-битный символ не "вписывается" в знаковый short
.
num('5'); // num(long)
Также следует указать, что при вызове с аргументом типа Long
будет вызвана версия метода, принимающая Object
-- этот метод c общим типом-предком более подходящий, чем вариант с анбоксингом:
num(Long.valueOf(10)); // num(Object);