Почему два метода с подписями (примитив, оболочка) и (примитивные, примитивные) вызывают неоднозначность вызова метода (обертка, примитив)?
Это просто упражнение, но я не могу понять двусмысленность:
private static void flipFlop(String str, int i, Integer iRef) {
System.out.println(str + "ciao");
}
private static void flipFlop(String str, int i, int j) {
System.out.println(str + "hello");
}
public static void main(String[] args) {
flipFlop("hello", new Integer(4), 2004);
}
В нем говорится:
Метод flipFlop (String, int, Integer) неоднозначен для типа Тест
Я бы догадался, что второй аргумент был бы распакован на int, и таким образом был бы выбран второй метод flipFlop
.
Ответы
Ответ 1
Если вам нравится захватывающее чтение, вот соответствующая часть спецификации Java Language, которая описывает, как методы разрешаются.
Но в основном ваш третий аргумент может быть интерпретирован как примитив или autoboxed wrapper, и компилятор не может понять, что вы хотите. Оба метода являются "максимально конкретными" для использования терминологии JLS.
Ответ 2
Хорошо, я более подробно рассмотрел JLS, и я считаю, что это должно устранить любые оставшиеся сомнения, которые могут возникнуть.
Это оригинальная проблема:
public class Main {
private static void flipFlop(int i, Integer iRef) {
System.out.println("Method 1");
}
private static void flipFlop(int i, int j) {
System.out.println("Method 2");
}
public static void main(String[] args) {
flipFlop(new Integer(4), 2004);
}
}
Как было указано в другом ответе: это терпит неудачу, потому что компилятор не может решить, какую перегрузку использовать.
Однако вы можете подумать, что это не имеет никакого смысла. В этой ситуации компилятор может решить, какой метод он должен использовать:
public class Main {
private static void flipFlop(Integer y) {
System.out.println("ciao");
}
private static void flipFlop(int j) {
System.out.println("hello");
}
public static void main(String[] args) {
flipFlop(new Integer(6));
flipFlop(6);
}
}
Рациональность говорит нам, что когда у вас есть значения X
+ Y
и два метода, которые принимают соответственно Y
+ X
и Y
+ Y
, и вы знаете, что X
и Y
являются взаимозаменяемыми, то это будет означать, что последний метод более конкретный.
Разница между этими двумя параметрами описана в JLS. Я представил весь рабочий процесс ниже, но важно следующее:
Сначала компилятор будет искать методы с равной подписью, а запрещать бокс/распаковку. В нашем втором примере это не вызывает никаких проблем, но в нашем первом примере это не возвращает выполнимый метод, так как ни один из них не принимает Integer
в качестве первого параметра.
Если это не удалось, компилятор переходит на второй шаг, где позволяет боксировать/распаковывать. Это должно устранить проблему, с которой мы столкнулись с первым параметром, но теперь вызывает неоднозначность со вторым параметром, поскольку теперь неясно, ссылаетесь ли вы на перегрузку с помощью int
или той, которая использует Integer
.
Это в конечном итоге приводит к двусмысленному вызову метода.
15.12.2. Время компиляции Шаг 2: определение подписи метода
-
Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения преобразования бокса или распаковки, или использование вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до второй фазы.
-
Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки , разрешая бокс и распаковку, но все же исключает использование вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до третьей фазы.
Если на одной из трех фаз тестирования применимости было идентифицировано несколько применимых методов, тогда выбирается наиболее конкретный, как указано в разделе §15.12. 2.5.
15.12.2.5. Выбор наиболее конкретного метода
Метод считается максимально специфичным для вызова метода, если он доступен и применим, и нет другого применимого и доступного метода, который является более конкретным.
Возможно, что ни один из методов не является наиболее конкретным, поскольку существуют два или более метода, которые являются максимально конкретными. В этом случае:
-
Если все максимально специфические методы имеют эквивалентную эквивалентную (§8.4.2), то: (... некоторые правила, чтобы решить, кто выбрал...)
-
В противном случае мы говорим, что вызов метода неоднозначен и возникает ошибка времени компиляции.
Ответ 3
Второй аргумент 2004
, который является int
, также применим к Integer
из-за автоматического бокса, вот почему компилятор не может решить, какой метод использовать.