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

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

Случай 1

static void call(Integer i) {
    System.out.println("hi" + i);
}

static void call(int i) {
    System.out.println("hello" + i);
}

public static void main(String... args) {
    call(10);
}

Результат случая 1: hello10

Случай 2

static void call(Integer... i) {
    System.out.println("hi" + i);
}

static void call(int... i) {
    System.out.println("hello" + i);
}

public static void main(String... args) {
    call(10);
}

Показывает ошибку компиляции reference to call ambiguous. Но я не мог понять. Зачем? Но, когда я прокомментировал какой-либо из методов call() из Case 2, тогда он отлично работает. Может ли кто-нибудь помочь мне понять, что здесь происходит?

4b9b3361

Ответ 1

Поиск наиболее конкретного метода определяется очень формальным образом в Java Language Specificaion (JLS). Я извлек ниже основные пункты, которые применяются при попытке удалить формальные формулы как можно больше.

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

  • JLS 15.12.2: ваш вариант использования относится к фазе 3:

Третья фаза (§15.12.2.4) позволяет комбинировать перегрузку с методами переменной arity, боксом и распаковкой.

  • Затем JLS 15.12.2.4 в основном определяет, что оба метода применимы, поскольку 10 можно преобразовать как в Integer..., так и в int.... Все идет нормально. И пункт заключает:

Наиболее специфический метод (§15.12.2.5) выбирается среди применимых методов переменной-arity.

  • Что приводит нас к JLS 15.12.2.5. В этом параграфе приведены условия, при которых метод arity m(a...) более специфичен, чем другой метод arity m(b...). В вашем случае использования с одним параметром и без дженериков он сводится к следующему:

m(a...) более специфичен, чем m(b...) iif a <: b, где <: означает is a subtype of.

Бывает, что int не является подтипом Integer, а Integer не является подтипом int.

Для использования языка JLS оба метода call поэтому максимально конкретны (никакой метод не является более конкретным, чем другой). В этом случае тот же абзац заключает:

  • Если все максимально конкретные методы имеют переопределяющие эквиваленты (§8.4.2) сигнатуры [...] = > не ваш случай, так как не используются дженерики, а Integer и int - разные параметры.
  • В противном случае мы говорим, что вызов метода неоднозначен и возникает ошибка времени компиляции.

Примечание

Если вы заменили Integer... на long..., например, у вас было бы int <: long, а наиболее конкретным методом было бы call(int...) *.
Точно так же, если вы заменили int... на Number..., метод call(Integer...) был бы наиболее конкретным.

* Фактически была ошибка в JDK до Java 7, которая показала бы двусмысленный вызов в этой ситуации.

Ответ 2

Похоже, что это связано с ошибка # 6886431, которая, кажется, исправлена ​​в OpenJDK 7.

Ниже приведено описание ошибки,

Описание ошибки:

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

int f(Object... args);
int f(int... args);

javac рассматривает второй как более конкретный, чем первый. Эта поведение разумно (я предпочитаю), но не согласуется с JLS (15.12.2).

Ответ 3

из JLS 15.12.2.2

JLS 15.12.2.2 Выберите наиболее специфический метод

IIf более чем одно объявление метода доступно и доступно к вызову метода, необходимо выбрать один для обеспечения дескриптор для отправки времени выполнения. Программирование на Java язык использует правило, в котором выбран наиболее специфический метод. неформальная интуиция заключается в том, что одно определение метода более конкретно чем другой, если любое обращение, обработанное первым методом, может быть передается другому, без ошибки типа компиляции.

ни один из этих методов не может быть передан другому (типы для int [] и Integer [] arent связаны), следовательно, вызов неоднозначен

Ответ 4

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

public static void main(String... args) {
  call((int)10);
  call(new Integer(10));
}

EDIT:

Это потому, что компилятор пытается преобразовать Integer в int, поэтому неявный листинг происходит до вызова метода call. Поэтому компилятор затем ищет любые методы с таким именем, которые могут принимать ints. И у вас их 2, поэтому компилятор не знает, какой из них должен быть вызван.

Ответ 5

Если применимо более одного метода, чем из спецификации языка Java, "Выбор наиболее конкретного метода", пункт 15.12.2.5:

Один элемент элемента переменной arty с именем m более конкретный, чем другой метод элемента с переменной переменной с тем же именем, если либо (<: means subtyping):

  • Один членный метод имеет n параметров, а другой имеет k параметров, где n ≥ k, и:
    • Типы параметров первого метода-члена - T1,..., Tn-1, Tn []. (мы имеем только один T_n [], который является Integer [], n = 1)
    • Типы параметров другого метода: U1,..., Uk-1, Uk []. (снова только один параграф, который является int [], k = 1)
    • Если второй метод является общим, то пусть R1... Rp (p ≥ 1) - его параметры типа, пусть Bl - объявленная граница Rl (1 ≤ l ≤ p), A1... Ap - это (§15.12.2.7) для этого вызова при начальных ограничениях Ti < Ui (1 ≤ я ≤ k-1) и Ti < Uk (k ≤ я ≤ n) и Si = Ui [R1 = A1,..., Rp = Ap] (1 ≤ я ≤ k). (метод не является общим)
    • В противном случае пусть Si = Ui (1 ≤ я ≤ k). (S1 = int [])
    • Для всех j от 1 до k-1, Tj <: Sj и (ничего здесь)
    • Для всех j от k до n, Tj <: Sk и (Сравнить T1 <: S1, Integer [] <: int [])
    • Если второй метод является общим методом, описанным выше, тогда Al <: Bl [R1 = A1,..., Rp = Ap] (1 ≤ l ≤ p). (метод не является общим)

Хотя примитивный int является autoboxed для обертки Integer, int[] не автобоксируется до Integer[], чем первое условие не выполняется.

Второе условие почти то же самое.

Существуют и другие условия, которые не выполняются, а затем из-за JLS:

мы говорим, что вызов метода неоднозначен, и возникает ошибка времени компиляции.

Ответ 6

Этот вопрос уже был задан несколько раз. Сложная часть состоит в том, что f(1, 2, 3) явно проходит int, поэтому почему компилятор не может выбрать версию f(int...)? Ответ должен лежать где-то в JLS, который я царапаю себе головы против

В соответствии с §15.12.2.4 оба метода применимы для метода переменной-переменной, поэтому следующим шагом будет определение наиболее конкретного.

К сожалению, §15.12.2.5 использует тест подтипа T i <: S i между f1 (T 1,.. T n) и f2 (S 1,.. S n) для определения целевого метода, и поскольку между Integer и int нет отношения подтипа, никто не выигрывает, потому что ни int: > Integer, ни Integer: > int. В конце параграфа указано:

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

Метод m1 строго конкретный, чем другой метод m2, если и только если m1 более конкретно, чем m2 и m2 не является более конкретным чем m1.

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

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

  • [...]

  • В противном случае мы говорим, что вызов метода неоднозначен и возникает ошибка времени компиляции.

Прикреплено сообщение в блоге от Gilad Bracha (см. выставку 2), в свою очередь связанное в отчете об ошибке из ответа @Jayamhona.