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

Почему компилятор предпочитает перегрузку int для перегрузки varargs char для char?

код

public class TestOverload {

    public TestOverload(int i){System.out.println("Int");}
    public TestOverload(char... c){System.out.println("char");}

    public static void main(String[] args) {
        new TestOverload('a');
        new TestOverload(65);
    }
}

Выход

Int
Int

Ожидается ли поведение? Если да, то почему? Я ожидаю: char, Int

Примечание. Я использую Java 8

4b9b3361

Ответ 1

Методы с varargs (...) имеют самый низкий приоритет, когда компилятор определяет, какой перегруженный метод выбрать. Поэтому TestOverload(int i) выбирается над TestOverload(char... c), когда вы вызываете TestOverload с одним параметром char 'a', так как a char может автоматически повышаться до int.

JLS 15.12.2:

  • Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения бокса или распаковки преобразования или использование переменной arity вызов метода. Если на этом этапе не обнаружен какой-либо применимый метод то обработка продолжается до второй фазы. Это гарантирует, что любые вызовы, которые были действительны в Java-программировании язык перед Java SE 5.0 не считается неоднозначным в качестве результата введения методов переменной arity, неявного бокса и/или распаковка. Однако объявление метода переменной arity (§8.4.1) может изменить метод, выбранный для вызова метода метода выражение, поскольку метод переменной arity рассматривается как фиксированный arity в первой фазе. Например, объявление m (Object...) в классе, который уже объявляет m (Object), вызывает m (Object) no более длинные выбираются для некоторых выражений вызова (например, m (null)), так как m (Объект []) более конкретный.

  • Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки при разрешении бокса и распаковки, но все еще исключает использование вызова метода переменной arity. Если в течение этого затем обработка продолжается до третьей фазы. Это гарантирует, что метод никогда не выбирается через переменную arity вызов метода, если он применим с помощью метода фиксированной arity вызов.

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

EDIT:

Вы хотите заставить компилятор вызвать конструктор TestOverload(char... c), вы можете передать вызов конструктора a char[]:

new TestOverload (new char[] {'a'});

Ответ 2

Да, это ожидаемое поведение. Приоритет вызова метода выглядит следующим образом:

  • Widending
  • Бокс
  • переменные аргументы

Ниже выдержки из Документов Java, связанных с тем же: -

Процесс определения применимости начинается с определения потенциально применимых методов (§15.12.2.1).

Остальная часть процесса разбивается на три фазы, чтобы обеспечить совместимость с версиями языка программирования Java до Java SE 5.0. Фазы:

Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения преобразования бокса или распаковки или использования вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до второй фазы.

Это гарантирует, что любые вызовы, которые были действительны на языке программирования Java до Java SE 5.0, не считаются двусмысленными в результате внедрения методов переменной arity, неявного бокса и/или распаковки. Однако объявление метода переменной arity (§8.4.1) может изменить метод, выбранный для выражения вызова метода данного метода, поскольку метод переменной arity рассматривается как метод фиксированной arity в первой фазе. Например, объявление m (Object...) в классе, который уже объявляет m (Object), приводит к тому, что m (Object) больше не выбирается для некоторых выражений вызова (таких как m (null)), как m (Object []) ) является более конкретным.

Вторая фаза (§15.12.2.3) выполняет разрешение перегрузки при разрешении бокса и распаковки, но все же исключает использование вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до третьей фазы.

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

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

Ответ 3

Твердые советы от Джошуа Блоха (Эффективная Java, 2-е изд.):

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

Объект с радикально отличающимся типом - это тот, который нельзя разумно отличить от другого типа аргументов. Следуя этому правилу, вы можете сэкономить часы отладки таинственной ошибки, которая может произойти, когда компилятор во время компиляции выбирает перегрузку метода, которую вы не ожидали.

Ваши строки кода нарушают это правило и открывают дверь для ошибок:

public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}

A char является взаимозависимым с int, и поэтому единственный способ предсказать, что произойдет с вызовами, - перейти к Спецификации языка Java и прочитать несколько загадочные правила о том, как разрешаются перегрузки.

К счастью, эта ситуация не должна нуждаться в исследовании JLS. Если у вас есть аргументы, которые не сильно отличаются друг от друга, возможно, лучшим вариантом является не перегрузка. Дайте методам разные имена, чтобы не было возможности для ошибок или путаницы со стороны любого, кому может понадобиться поддерживать код.

Время - деньги.

Ответ 4

Я взял код этой ссылки и изменил некоторые его части:

    public static void main(String[] args) {
    Byte i = 5;
    byte k = 5;
    aMethod(i, k);
}

//method 1
static void aMethod(byte i, Byte k) {
    System.out.println("Inside 1");
}

//method 2
static void aMethod(byte i, int k) {
    System.out.println("Inside 2");
}

//method 3
static void aMethod(Byte i, Byte k) {
    System.out.println("Inside 3 ");
}

//method 4
static void aMethod(Byte  i, Byte ... k) {
    System.out.println("Inside 4 ");
}

Компилятор дает ошибку (метод неоднозначен для типа Перегрузка) для методов 1, 2 и 3, но не 4 (почему?)

Ответ заключается в механизме, который java использует для сопоставления вызовов методам сигнатур методов. Механизм выполняется в три этапа, в каждой фазе, если он находит способ совпадения, он останавливается:

+ фаза первая: используйте расширение, чтобы найти метод сопоставления (найдены совпадающие методы)

+ вторая фаза: (также) используйте бокс/распаковку для поиска метода соответствия (соответствие метода 1,2 и 3)

+ фаза три: (также) используйте var args (метод 4 соответствует!)