2017-02-04 20 views
2

Когда перегрузка метода завершена, я знаю, что мы можем создавать только методы с тем же именем, если только их сигнатуры методов отличаются.При передаче аргументов в методы становятся двусмысленными в 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 и двойные типы после автоматического преобразования и это сбивает с толку, какие метод значения передаются правильно? Пожалуйста, исправьте меня, если я ошибаюсь ..

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

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 и double right? выход был задан как myMethod (int) .. не должно это путать с компилятором и давать эту ошибку ссылка на myMethod неоднозначна ??

+3

Короткий ответ: Java работает таким образом в соответствии с JLS. http://stackoverflow.com/questions/30776256/casting-rules-for-primitive-types-in-java –

+0

@ eug.nikolaev на самом деле этот ответ не отвечает на текущий вопрос, который шире, чем расширяющийся примитив переходы имеют значение. Это дает довольно интересные указатели. – davidxxx

ответ

1

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

В 15.12.2.5. Choosing the Most Specific Method, у вас есть ценная информация:

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

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

One fixed-arity member method named m is more specific than another member method of the same name and arity if all of the following conditions hold:

  • The declared types of the parameters of the first member method are T1, ..., Tn.

  • The declared types of the parameters of the other method are U1, ..., Un.

  • If the second method is generic, then let R1 ... Rp (p ≥ 1) be its type parameters, let Bl be the declared bound of Rl (1 ≤ l ≤ p), let A1 ... Ap be the type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ti << Ui (1 ≤ i ≤ n), and let Si = Ui[R1=A1,...,Rp=Ap] (1 ≤ i ≤ n).

    Otherwise, let Si = Ui (1 ≤ i ≤ n).

  • For all j from 1 to n, Tj <: Sj.

  • If the second method is a generic method as described above, then Al <: Bl[R1=A1,...,Rp=Ap] (1 ≤ l ≤ p).

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

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

Вы можете найти эту информацию в JLS : 15.12.2. Compile-Time Step 2: Determine Method Signature:

A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific.

If there is exactly one maximally specific method, then that method is in fact the most specific method; it is necessarily more specific than any other accessible method that is applicable. It is then subjected to some further compile-time checks as described in §15.12.3.

It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:

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

  • If exactly one of the maximally specific methods is not declared abstract, it is the most specific method.

  • Otherwise, if all the maximally specific methods are declared abstract, and the signatures of all of the maximally specific methods have the same erasure (§4.6), then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type.

  • However, the most specific method is considered to throw a checked exception if and only if that exception or its erasure is declared in the throws clauses of each of the maximally specific methods.

- Otherwise, we say that the method invocation is ambiguous, and a compile-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), производит ошибку компиляции по той же причине.

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

-1

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

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

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

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

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); 
    } 
} 

Выходной файл 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 has int` в декларации. Пример того, что я говорю

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

0

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