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

Дело 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); } по 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 , он работает нормально. Может ли кто-нибудь помочь мне понять, что здесь происходит?

Поиск наиболее специфического метода определяется очень формальным образом в 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...) более конкретный, чем другой метод 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, которая показала бы двусмысленный вызов в этой ситуации .

Похоже, что это связано с ошибкой # 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 ):

  1. Один метод-член имеет 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.

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

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

  1. […]

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

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

  • Общий метод Выполняется со временем выполнения
  • Почему Mockito не издевается над статическими методами?
  • Получение имени текущего исполняемого метода
  • Считать слова в строковом методе?
  • Объект Bound и unbound method в Python
  • Реализовать чистый виртуальный метод в Objective-C
  • В чем разница между .replace и -replace в powershell?
  • Преобразование целых чисел в римские цифры - Java
  • Вызов метода перехвата в Objective-C
  • Как передать несколько параметров в Objective-C?
  • Что делает ключевое слово return в методе void в Java?
  • Interesting Posts

    В чем разница между col-lg- *, col-md- * и col-sm- * в Bootstrap?

    Снижается ли производительность при выполнении циклов, чей счетчик uop не кратен ширине процессора?

    Нужно ли ВСЕ использовать виртуальные функции в производных classах?

    Как обновить несколько элементов массива в mongodb

    Y в группе «Маленькая схема»

    Что такое генерация ключей в Google Chrome?

    Невозможно загрузить обновленный APK в Google Play из-за другого сертификата

    Простая структура для шейдеров OpenGL в C / C ++

    Как поддерживать несколько версий Android в коде?

    Как я могу разместить свой веб-сайт в Windows Vista на домашнем компьютере?

    Папка «Мои документы» для нового местоположения в Windows 7

    Что именно происходит, когда я устанавливаю LoadUserProfile пула IIS?

    Javafx: Разница между javafx.concurent и Platform.runLater?

    В Vim есть способ удалить, не помещая текст в регистр?

    Как я могу исправить уязвимость shellshock в устаревшей системе Ubuntu, которую я не могу обновить?

    Давайте будем гением компьютера.