Ошибка компилятора: ссылка на вызов неоднозначную
Дело 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
- Почему не вызывает статический метод путем экземпляра ошибки для компилятора Java?
- Как использовать переменную одного метода в другом методе?
- В Java, в чем разница между this.method () и методом ()?
- Ошибка удаления гибернации: пакетное обновление возвращено Неожиданное количество строк
- Если частные вспомогательные методы являются статическими, если они могут быть статическими
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); }
по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
, он работает нормально. Может ли кто-нибудь помочь мне понять, что здесь происходит?
- C # Делегирование делегата или просто передача ссылки на метод
- Обнаружить версию целевой рамок во время компиляции
- Почему компилятор Scala запрещает перегруженные методы с аргументами по умолчанию?
- Конечные аргументы в методах интерфейса - в чем смысл?
- Как отобразить все методы объекта?
- В чем разница между методом и функцией?
- Что такое метод вложения?
- Прочитать значение атрибута метода
Поиск наиболее специфического метода определяется очень формальным образом в 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) выбирается среди применимых методов переменной.
- Это подводит нас к JLS 15.12.2.5 . Этот параграф дает условия, при которых метод arity
m(a...)
более конкретный, чем другой метод aritym(b...)
. В вашем случае использования с одним параметром и без дженериков он сводится к:
m(a...)
более специфично, чемm(b...)
iifa <: 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, которая показала бы двусмысленный вызов в этой ситуации .
Похоже, что это связано с ошибкой # 6886431 , которая, похоже, исправлена в OpenJDK 7.
Ниже приведено описание ошибки,
Ошибка Описание:
При вызове метода со следующими перегруженными сигнатурами я ожидаю ошибку двусмысленности (при условии, что аргументы совместимы с обоими):
int f(Object... args); int f(int... args);
javac рассматривает второй как более конкретный, чем первый. Такое поведение разумно (я предпочитаю), но не согласуется с JLS (15.12.2).
от JLS 15.12.2.2
JLS 15.12.2.2. Выберите наиболее конкретный метод
Если более чем одно объявление метода доступно и доступно для вызова метода, необходимо выбрать его для предоставления дескриптора для отправки времени выполнения. Язык программирования Java использует правило, в котором выбран наиболее специфический метод. Неофициальная интуиция заключается в том, что одно объявление метода более конкретное, чем другое, если любое обращение, обработанное первым методом, может быть передано другому, без ошибки типа компиляции.
ни один из этих методов не может быть передан другому (типы для int [] и Integer [] arent связаны), следовательно, вызов неоднозначен
Компилятор не знает, какой метод следует вызывать. Чтобы исправить это, вам нужно ввести входные параметры.
public static void main(String... args) { call((int)10); call(new Integer(10)); }
РЕДАКТИРОВАТЬ:
Это связано с тем, что компилятор пытается преобразовать Integer в int, поэтому перед call
метода call
происходит неявное литье. Поэтому компилятор затем ищет любые методы с таким именем, которые могут принимать ints. И у вас их 2, поэтому компилятор не знает, какая из них должна быть вызвана.
Если применимо более одного метода, чем в Спецификации языка Java, который мы выбираем наиболее конкретный метод , пункт 15.12.2.5
:
Один метод члена переменной arty с именем m
более специфичен, чем другой метод члена переменной arty с тем же именем, если либо ( <: 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 ≤ i ≤ k-1) и Ti << Uk (k ≤ i ≤ n), а Si = Ui [R1 = A1 ,. .., Rp = Ap] (1 ≤ i ≤ k). ( метод не является общим )
- В противном случае пусть Si = Ui (1 ≤ i ≤ 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:
мы говорим, что вызов метода неоднозначен, и возникает ошибка времени компиляции.
Этот вопрос уже задавался несколько раз. Сложная часть состоит в том, что f(1, 2, 3)
явно пропускает int
, поэтому почему компилятор не может выбрать версию f(int...)
? Ответ должен лежать где-то в JLS , и я почесываю голову против
Согласно п. 15.12.2.4, оба метода применимы к методу переменной arity , поэтому следующим шагом будет определение наиболее конкретного.
К сожалению, в §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.