Error del comstackdor: referencia a llamada ambigua

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

Salida del Caso 1: hello10

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

Muestra la reference to call ambiguous error de comstackción reference to call ambiguous . Pero, no pude entender. Por qué ? Pero cuando comencé a comentar cualquiera de los métodos call() del Case 2 , funciona bien. ¿Alguien puede ayudarme a entender lo que está sucediendo aquí?

Encontrar el método más específico se define de forma muy formal en la especificación de lenguaje de Java (JLS). He extraído debajo de los elementos principales que se aplican mientras trato de eliminar las fórmulas formales tanto como sea posible.

En resumen, los elementos principales que se aplican a sus preguntas son:

  • JLS 15.12.2 : su caso de uso cae en la fase 3:

La tercera fase (§15.12.2.4) permite que la sobrecarga se combine con métodos de aridad variable, boxeo y desempaquetado.

  • Entonces, JLS 15.12.2.4 básicamente determina que ambos métodos son aplicables, porque 10 se puede convertir a un Integer... o a un int... Hasta aquí todo bien. Y el párrafo concluye:

El método más específico (§15.12.2.5) se elige entre los métodos de aria variable aplicables.

  • Lo que nos lleva a JLS 15.12.2.5 . Este párrafo proporciona las condiciones bajo las cuales un método arity m(a...) es más específico que otro método arity m(b...) . En su caso de uso con un parámetro y sin generics, se reduce a:

m(a...) es más específico que m(b...) iif a <: b , donde <: means is a subtype of .

Ocurre que int no es un subtipo de Integer y Integer no es un subtipo de int .

Para usar el lenguaje JLS, ambos métodos de call son, por lo tanto, más específicos (ningún método es más específico que el otro). En este caso, el mismo párrafo concluye:

  • Si todos los métodos de máxima especificidad tienen sustituciones equivalentes (§8.4.2) [...] => no es su caso, ya que no están involucrados los generics y el entero e int son parámetros diferentes
  • De lo contrario, diremos que la invocación del método es ambigua y se produce un error en tiempo de comstackción.

NOTA

Si reemplazó Integer... por long... por ejemplo, tendría int <: long y el método más específico sería call(int...) *.
De forma similar, si reemplazó int... por Number... , el método de call(Integer...) sería el más específico.

* En realidad, había un error en JDK antes de Java 7 que mostraría una llamada ambigua en esa situación .

Parece que está relacionado con el error # 6886431 , que parece estar arreglado en OpenJDK 7.

A continuación está la descripción del error,

Descripción del error:

Al invocar un método con las siguientes firmas sobrecargadas, espero un error de ambigüedad (suponiendo que los argumentos sean compatibles con ambos):

 int f(Object... args); int f(int... args); 

javac trata el segundo como más específico que el primero. Este comportamiento es razonable (lo prefiero), pero es inconsistente con el JLS (15.12.2).

de JLS 15.12.2.2

JLS 15.12.2.2 Elija el método más específico

Si más de una statement de método es accesible y aplicable a una invocación a un método, es necesario elegir una para proporcionar el descriptor para el envío del método en tiempo de ejecución. El lenguaje de progtwigción Java usa la regla de que se elige el método más específico. La intuición informal es que una statement de método es más específica que otra si cualquier invocación manejada por el primer método podría pasarse a la otra sin un error de tipo de tiempo de comstackción.

ninguno de estos métodos se puede pasar al otro (los tipos para int [] y entero [] no están relacionados) por lo tanto, la llamada es ambigua

El comstackdor no sabe a qué método se debe llamar. Para solucionar esto, necesita convertir los parámetros de entrada.

 public static void main(String... args) { call((int)10); call(new Integer(10)); } 

EDITAR:

Es porque el comstackdor intenta convertir el entero a int. Por lo tanto, se produce un lanzamiento implícito antes de la invocación del método de call . Entonces, el comstackdor busca cualquier método con ese nombre que pueda tomar ints. Y tiene 2 de ellos, por lo que el comstackdor no sabe cuál de los dos debería llamarse.

Si puede aplicarse más de un método, que a partir de la Especificación del lenguaje Java , 15.12.2.5 método más específico , párrafo 15.12.2.5 :

Un método miembro de arity variable llamado m es más específico que otro método de miembro de arity variable del mismo nombre si cualquiera ( <: means subtyping ):

  1. El método de un miembro tiene n parámetros y el otro tiene k parámetros, donde n ≥ k y:
    • Los tipos de parámetros del primer método miembro son T1, ..., Tn-1, Tn []. ( tenemos solo un T_n [], que es Integer [], n = 1 )
    • Los tipos de parámetros del otro método son U1, ..., Uk-1, Uk []. (de nuevo solo un paramenter, que es int [], k = 1 )
    • Si el segundo método es genérico, entonces deje que R1 ... Rp (p ≥ 1) sea su tipo de parámetros, deje que Bl sea el límite declarado de R1 (1 ≤ l ≤ p), deje que A1 ... Ap sea el tipo de argumentos inferidos (§15.12.2.7) para esta invocación bajo las restricciones iniciales Ti << Ui (1 ≤ i ≤ k-1) y Ti << Uk (k ≤ i ≤ n), y sea Si = Ui [R1 = A1 ,. .., Rp = Ap] (1 ≤ i ≤ k). (el método no es genérico )
    • De lo contrario, deje Si = Ui (1 ≤ i ≤ k). ( S1 = int [] )
    • Para todos j de 1 a k-1, Tj <: Sj, y, ( nada aquí )
    • Para todo j de k a n, Tj <: Sk, y, ( Compare T1 <: S1, Integer [] <: int [] )
    • Si el segundo método es un método genérico como se describió anteriormente, entonces Al <: Bl [R1 = A1, ..., Rp = Ap] (1 ≤ l ≤ p). (el método no es genérico )

Aunque primitive int se autoboxe para envolver Integer , int[] no se autoboxea en Integer[] , de lo que no se cumple la primera condición.

La segunda condición es casi la misma.

También hay otras condiciones que no se cumplen, y luego debido a JLS:

decimos que la invocación del método es ambigua y se produce un error en tiempo de comstackción.

Esta pregunta ya ha sido formulada varias veces. La parte difícil es que f(1, 2, 3) claramente está pasando int , ¿por qué el comstackdor no puede elegir la versión f(int...) ? La respuesta debe estar en algún lugar del JLS , que me estoy rascando la cabeza

De acuerdo con §15.12.2.4, ambos métodos son aplicables al método de aria variable , por lo que el siguiente paso es identificar el más específico.

Desafortunadamente, §15.12.2.5 usa la prueba de subtipo T i <: S i entre los parámetros formales f1 (T 1 , .. T n ) y f2 (S 1 , .. S n ) para identificar el método objective, y dado que no existe ninguna relación de subtipo entre Integer e int , nadie gana , porque ni int:> Integer ni Integer:> int . Al final del párrafo se establece:

Las condiciones anteriores son las únicas circunstancias bajo las cuales un método puede ser más específico que otro. […]

Un método m1 es estrictamente más específico que otro método m2 si y solo si m1 es más específico que m2 y m2 no es más específico que m1.

Se dice que un método es lo más específico para una invocación a un método si es accesible y aplicable, y no existe otro método que sea aplicable y accesible que sea estrictamente más específico.

Es posible que ningún método sea el más específico, porque hay dos o más métodos que son específicos al máximo. En este caso:

  1. […]

  2. De lo contrario, diremos que la invocación del método es ambigua y se produce un error en tiempo de comstackción.

Adjunta una publicación de blog de Gilad Bracha (ver exhibit 2), a su vez vinculada en el informe de errores de la respuesta de @ Jayamhona.