Когда передача аргументов в методы становится неоднозначной в Java?

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

class Demo{
    public static void myMethod(int y, double x){}
    public static void myMethod(double x,int y){}

    public static void main(String args[]){
        byte b=10;
        myMethod(b,b);
    }
}

Приведенный выше код выдает ошибку, в которой говорится об ошибке: ссылка на myMethod неоднозначна. Эта проблема возникает из-за того, что значение байта может быть присвоено как типу int, так и типу double после автоматического преобразования, и возникает путаница относительно того, какой метод должен передавать значения, верно? Пожалуйста, поправьте меня, если я ошибаюсь..

Я попробовал следующую программу. Я думал, что это также даст ошибку, но она скомпилирована без ошибки

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        MyClass.myMethod(100);
    }
}

Я думал, что это также выдаст ту же ошибку, что и ранее, но это выдает вывод как myMethod (int)... поэтому я предположил, что, поскольку он имеет идеально подходящий метод, который может передавать значение int, он не выдает ошибку.,

но что, если я внесу следующие изменения во вторую программу выше, почему она не выдаст ошибку??

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        byte b=10;
        MyClass.myMethod(b);
    }
}

байт может быть автоматически преобразован в int и удвоен, верно? выходные данные были заданы как myMethod (int).. разве это не должно сбивать с толку компилятора и дать ссылку на ошибку myMethod неоднозначно??

4 ответа

Решение

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

В 15.12.2.5. Выбирая наиболее специфический метод, вы получаете ценную информацию:

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

В вашем примере вот часть JLS, которая должна ответить на ваш вопрос:

Один метод-член с фиксированной арностью с именем m более специфичен, чем другой метод-член с тем же именем и арностью, если выполняются все следующие условия:

  • Объявленные типы параметров первого метода-члена: T1, ..., Tn.

  • Заявленные типы параметров другого метода: U1, ..., Un.

  • Если второй метод является общим, то пусть R1 ... Rp (p ≥ 1) будут параметрами его типа, пусть Bl будет объявленной границей Rl (1 ≤ l ≤ p), пусть A1 ... Ap будет аргументами типа вывод (§15.12.2.7) для этого вызова при начальных ограничениях Ti << Ui (1 ≤ i ≤ n), и пусть Si = Ui[R1=A1,...,Rp=Ap] (1 ≤ i ≤ n).

    В противном случае пусть Si = Ui (1 ≤ i ≤ n).

  • Для всех j от 1 до n Tj <: Sj.

  • Если второй метод является общим методом, как описано выше, то Al <: Bl [R1 = A1,..., Rp = Ap] (1 ≤ l ≤ p).

Что должно вас заинтересовать For all j from 1 to n, Tj <: Sj,

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

Вы можете найти эту информацию в JLS: 15.12.2. Шаг 2 времени компиляции: определение сигнатуры метода:

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

Если существует ровно один максимально специфический метод, то этот метод на самом деле является наиболее специфичным методом; он обязательно более конкретен, чем любой другой доступный метод, который применим. Затем он подвергается некоторым дополнительным проверкам во время компиляции, как описано в §15.12.3.

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

- If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then:

  • Если точно один из максимально специфических методов не объявлен абстрактным, это самый специфический метод.

  • В противном случае, если все максимально специфические методы объявлены абстрактными, а сигнатуры всех максимально специфических методов имеют одинаковое стирание (§4.6), то наиболее специфический метод выбирается произвольно среди подмножества максимально специфических методов, которые имеют наиболее конкретный тип возврата.

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

- Otherwise, we say that the method invocation is ambiguous, and acompile-time > error occurs.

Если мы применим эти правила к вашим трем примерам (я изменил порядок, чтобы начать с случаев, которые хорошо компилируются и заканчиваются с ошибкой компиляции).

1) Найден один максимально специфический метод

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        MyClass.myMethod(100);
    }
}

компилятор видит метод с идеальным соответствием: myMethod(int i) поскольку переданное значение является int, Компилируется нормально.

2) Найден один максимально специфический метод

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        byte b=10;
        MyClass.myMethod(b);
    }
}

Компилятор видит метод с более высокой специфичностью, чем другой.
Неявное преобразование из byte в int действительно более конкретный, чем неявное преобразование из byte в double согласно расширяющимся правилам примитивных преобразований.

Мы могли бы проверить это void myMethod(int i) более конкретно, чем void myMethod(double a) если любой вызов, обработанный первым методом, может быть передан другому без ошибки типа времени компиляции.

myMethod(3); применительно к void myMethod(double x) компилируется нормально.
Но myMethod(double)3); применительно к void myMethod(int y) выдает ошибку компиляции.
Так был найден уникальный максимально специфический метод: void myMethod(int i),
Компиляция в порядке.

3) Не найден уникальный максимально специфический метод

class Demo{
    public static void myMethod(int y, double x){}
    public static void myMethod(double x,int y){}

    public static void main(String args[]){
        byte b=10;
        myMethod(b,b);
    }
}

Компилятор видит два метода, где ни у кого нет специфичности выше, чем у другого.
Во-первых, в обоих случаях требуется неявное преобразование типов эффективных параметров в объявленные типы параметров методов.
Но в обоих случаях специфика одинакова.

Мы могли бы проверить это void myMethod(int y, double x) так же конкретно, как myMethod(double x,int y) если любой вызов, обработанный любым методом, не может быть передан другому без ошибки типа времени компиляции.

myMethod(double)3,4); применительно к void myMethod(int y, double x) выдает ошибку компиляции как int переменная не может принять double значение.
А также myMethod(3,(double)4); применительно к void myMethod(double x,int y) выдает ошибку компиляции по той же причине.

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

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        MyClass.myMethod(100);
    }
}

Выходные данные myMethod1(int), потому что тип по умолчанию для целочисленного литерала Java является целым числом. Так что он принимает ближайший матч и звонки myMethod(int i),

class MyClass{
    public static void myMethod(int i){
        System.out.println("myMethod1(int)");
    }
    public static void myMethod(double a){
        System.out.println("myMethod2(int)");
    }   
}

class Demo{
    public static void main(String args[]){
        byte b=10;
        MyClass.myMethod(b);
    }
}

Это не создаст неоднозначность для компилятора, поскольку отправляемый параметр является байтовым, а байт расширен в соответствии с параметром метода. Если бы вы заявили myMethod(short i), Это назвало бы это вместо myMethod(int i), Вот как расширение работает в Java.

byte->short->int->long

class Demo{
    public static void myMethod(int y, double x){}
    public static void myMethod(double x,int y){}

    public static void main(String args[]){
        byte b=10;
        myMethod(b,b);
    }
}

Компилятор находит неоднозначность в приведенном фрагменте как byte b расширена до int теперь оба метода имеют объявление параметров как (double,int) (int,double) так что есть неопределенность, так как обе декларации имеют int переменная, следовательно, создает путаницу для самого компилятора. Попробуйте изменить одно из объявлений метода на (double,double) and you'll see that it calls the one that hasint` в объявлении. Пример того, что я говорю,

class Demo{
    public static void myMethod(double y, double x){}
    public static void myMethod(double x,int y){}

    public static void main(String args[]){
        byte b=10;
        myMethod(b,b);
    }
}

В этом случае он позвонит myMethod(double x, int y)

Для большего разрешения вы можете обратиться к JLS

Он знает, предпочитать продвижение int за повышение до double, но вы дали ему выбор между двумя методами, которые оба требуют повышения до intтак что он не знает, кого ты имеешь в виду.

Чтобы понять такого рода проблемы, вы должны иметь в виду ниже трех:

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

Разработчики Java решили, что старый код должен работать точно так же, как он работал до того, как стали доступны функции распаковки и упаковки.

Расширение предпочтительнее, чем бокс / распаковка (из-за вышеизложенного), что, в свою очередь, предпочтительнее, чем var-args.

Другие вопросы по тегам