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

Неоднозначный вызов Varargs Java

Я немного запутался в методах Java varargs:

public static int sum(int ...a) {
    return 0;
}

public static double sum(double ...a) {
    return 0.0;
}

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

В отличие от этого, следующий фрагмент кода генерирует ошибку компилятора при попытке вызвать sum без каких-либо аргументов:

public static int sum(int ...a) {
    return 0;
}

public static boolean sum(boolean ...a) {
    return true;
}
4b9b3361

Ответ 1

Общее правило, которое применяется здесь, следующее: если одна подпись метода строго конкретна, чем другая, тогда Java выбирает ее без ошибок.

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

При представлении выбора между сигнатурами sum(int... args) и sum(double... args) подпись sum(int... args) более конкретна, поскольку любое обращение этого метода также может быть передано на sum(double... args) путем применения расширяющегося преобразования. То же самое не выполняется для метода sum(boolean... args), который нельзя преобразовать аналогичным образом.

Спецификация языка Java, версия SE 8:

15,12. Выражения вызова метода

15.12.2.5. Выбор наиболее конкретного метода

Язык программирования Java использует правило выбора самого конкретного метода.

...

Один применимый метод m1 более конкретный, чем другой применимый метод m2, для вызова с выражениями аргументов e1,..., ek, если выполнено одно из следующих утверждений:

...

  • m2 не является общим, а m1 и m2 применимы строгим или свободным вызовом и где m1 имеет формальные типы параметров S1,..., Sn и m2 имеет формальные типы параметров T1,..., Tn, тип Si более специфичен, чем Ti для аргумента ei для всех я (1 ≤ я ≤ n, n = k).

...

Тип S более специфичен, чем тип T для любого выражения, если S <: T (§ 4.10).


4,10. Подтипировании

4.10.1. Подтипирование среди примитивных типов

double > 1 float

float > 1 long

long > 1 int

Ответ 2

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

Цитата:

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

(Пусть переопределяет правило 1 следующим образом: "Примитивное расширение использует максимально возможный аргумент метода." )

Итак, имея в виду эти правила, мы можем получить представление о том, что происходит здесь:

Согласно правилу номер один, примитивное расширение использует максимально возможный аргумент метода. Поскольку int представляет нечетное число (например, 1), а double представляется десятичным числом с точностью 32 байта больше, чем значение float (например, 1.0), мы можем сказать, что int "меньше" или "меньше" double s, и по этой логике int можно "продвигать" до double, а double можно "понизить" до int s.

Проще говоря, примитив, который может быть расширен до другого примитива (например, intfloatdouble), более специфичен, чем другой. Например, int более специфичен, чем a double, потому что 1 может быть повышен до 1.0.

Если вы не передали никакие аргументы этим перегруженным методам vararg с тем же именем, так как возврат фактически тот же (0 и 0.0 соответственно), компилятор предпочтет использовать метод, который принимает vararg типа int, поскольку это более конкретно.

Итак, когда вы ввели те же методы, которые принимают в int и boolean (типы, которые не могут быть расширены друг к другу) соответственно, компилятор теперь не может выбрать метод для использования, поскольку int не может быть "продвигается" или "понижается", как int s, float и double s. Следовательно, это вызовет ошибку компиляции.

Надеюсь, это поможет вам понять, что происходит.