Подтвердить что ты не робот

Перегрузка Java с аргументами переменной длины

Почему в этом коде отсутствует ошибка компиляции:

public class OverloadingVarArgs
{
    public void fun1(int... b)
    {
        System.out.println("int");
    }
    public void fun1(long... a)
    {
        System.out.println("long");
    }
    public static void main(String[] args)
    {
        OverloadingVarArgs obj = new OverloadingVarArgs();
        obj.fun1();
    }

}

Но этот код дает ошибку компиляции!

public class OverloadingVarArgs
{
    public void fun1(int... b)
    {
        System.out.println("int");
    }
    public void fun1(boolean... a)
    {
        System.out.println("boolean");
    }
    public static void main(String[] args)
    {
        OverloadingVarArgs obj = new OverloadingVarArgs();
        obj.fun1();
    }
}

Я считаю, что в обоих случаях должна быть ошибка компиляции, но это не так.

4b9b3361

Ответ 1

Правила выбора правильного перегруженного метода следующие:

  • Примитивное расширение использует наименьший возможный аргумент метода
  • Тип обтекателя не может быть расширен до другого типа Wrapper
  • Вы можете вставить из int в Integer и расширить до Object, но не до Long
  • Расширение бьет Бокс, Бокс побеждает Вар-Аргса.
  • Вы можете вставить Box, а затем Widen (int может стать объектом через Integer)
  • Вы не можете Widen, а затем Box (An int не может стать длинным)
  • Вы не можете комбинировать var-args с расширением или боксом

Посмотрите на это последнее правило. Вы не можете комбинировать расширение или бокс с аргументами переменной длины. Это означает, что типы нельзя манипулировать каким-либо образом, и вам нужно выполнить сравнение как есть. int и long можно сравнить, без проблем, и компилятор может вывести, что int является меньшим из двух. Согласно первому правилу, он будет использоваться для наименьшего аргумента метода, поэтому он разработал правильный (и только) маршрут к методу.

Однако, когда вы переходите к boolean и int, между ними нет метода сравнения из-за сильной типизации Java. Не имея знания о том, какой тип является наименьшим, компилятор абсолютно не знает, какой метод вы имеете в виду.

Более визуальный пример

Позвольте сделать шаг за шагом с точки зрения компилятора. Во-первых, с int и long.

int и long

Шаг 1 - Проверка соответствия параметров любым аргументам, и если да, то какой из них соответствует точно

Ну, varargs означает, что вы можете передать 0 во многие аргументы. В этом случае вы решили передать аргументы 0, поэтому ваш вызов соответствует типу int и типу long.

Шаг 2 - попытка автобокса или расширения. Это должно помочь ему разобраться, какой из них нужно для

Вы используете varargs, поэтому компилятор знает, что он не может этого сделать, в соответствии с окончательным правилом.

Шаг 3 - Попытка выяснить, какой тип наименьший

Компилятор может сравнить тип int с типом long. Из этого следует, что int является наименьшим типом.

Шаг 4 - Вызов

С учетом того, что int является наименьшим типом, он затем передает значение методу для выполнения.

Хорошо, а теперь сделаем то же самое с boolean и int.

boolean и int

Шаг 1 - Проверка соответствия параметров любым аргументам, и если да, то какой из них соответствует точно

Та же история. Вы ничего не передали, так что оба аргумента совпадают.

Шаг 2 - попытка автобокса или расширения. Это должно помочь ему разобраться, какой из них нужно для

Как и выше, вам не разрешено делать это, потому что вы использовали varargs.

Шаг 3 - Попытка выяснить, какой тип наименьший

Это ключевое различие. Здесь типы не сопоставимы. Это означает, что компилятор не знает, какой метод вы хотите вызывать по вашим параметрам или по наименьшему типу. Ergo, он не смог выработать правильный маршрут.

Шаг 4 - Вызов

Без знания того, какой метод вызывать, он не может продолжить выполнение и выбрасывает соответствующее исключение.

Ответ 2

В вашем втором примере компилятор не может определить наиболее специфический метод для вызова.

Детали gory объясняются в спецификации языка, но по существу, если две переменные-arity (var-arg), тогда, если метод A может принимать аргументы, переданные методу B, но не наоборот, то наиболее вероятен метод B.

В вашем первом примере применяются правила примитивной подтипы, которые:

double > 1 float

float > 1 long

long > 1 int

int > 1 char

int > 1 short

short > 1 byte

(Where > 1 означает "прямой супертип" )

Здесь мы видим, что int более конкретный, чем a long, поэтому выбран ваш метод fun1(int... b).

Во втором примере компилятор выбирает между int и boolean. Между этими типами primiritve нет отношения подтипа, поэтому не существует особого метода, и "вызов метода неоднозначен и возникает ошибка времени компиляции". (последняя строка в 15.12.2.5).

Ответ 3

Если у вас есть int и long (сопоставимые типы), по умолчанию будет использоваться наименьшее значение, которое равно int (поскольку вы не передаете аргумент) - я думаю, потому что int может расширяться до long, но long не может (если вы явно не набросаете его), компилятор выберет наименьший тип точности.

Но если у вас есть boolean и int, сравнение не может быть выполнено, и вы получите

The method fun1(int[]) is ambiguous for the type OverloadingVarArgs