Erro do compilador: referência para chamar ambíguo

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

Saída do Caso 1: olá10

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

Mostra a reference to call ambiguous erro de compilation reference to call ambiguous . Mas eu não consegui entender. Por quê ? Mas, quando eu comentei qualquer um dos methods call() do Case 2 , então ele funciona bem. Alguém pode me ajudar a entender, o que está acontecendo aqui?

Encontrar o método mais específico é definido de uma maneira muito formal no Java Language Specificaion (JLS). Eu extraí abaixo os principais itens que se aplicam ao tentar remover as fórmulas formais, tanto quanto possível.

Em resumo, os principais itens que se aplicam às suas perguntas são:

  • JLS 15.12.2 : seu caso de uso está na fase 3:

A terceira fase (§15.12.2.4) permite que a sobrecarga seja combinada com methods de aridade variável, boxe e unboxing.

  • Então o JLS 15.12.2.4 basicamente determina que ambos os methods são aplicáveis, porque 10 pode ser convertido em um Integer... ou um int... Por enquanto, tudo bem. E o parágrafo conclui:

O método mais específico (§15.12.2.5) é escolhido entre os methods de igualdade variável aplicáveis.

  • O que nos leva ao JLS 15.12.2.5 . Este parágrafo dá as condições sob as quais um método de aridade m(a...) é mais específico que outro método de aridade m(b...) . No seu caso de uso com um parâmetro e nenhum genérico, resume-se a:

m(a...) é mais específico que m(b...) iif a <: b , onde <: significa is a subtype of .

Acontece que int não é um subtipo de Integer e Integer não é um subtipo de int .

Para usar a linguagem JLS, os dois methods de call são, portanto, maximamente específicos (nenhum método é mais específico que o outro). Nesse caso, o mesmo parágrafo conclui:

  • Se todos os methods maximamente específicos tiverem assinaturas equivalentes a override (§8.4.2) [...] => não o seu caso, já que não há genéricos envolvidos e Integer e int são parâmetros diferentes
  • Caso contrário, dizemos que a invocação do método é ambígua e ocorre um erro em tempo de compilation.

NOTA

Se você substituiu Integer... por long... por exemplo, você teria int <: long e o método mais específico seria call(int...) *.
Da mesma forma, se você substituiu int... por Number... , o método call(Integer...) seria o mais específico.

* Houve realmente um bug nos JDKs anteriores ao Java 7 que mostraria uma chamada ambígua nessa situação .

Parece que está relacionado com o bug # 6886431 , que parece estar corrigido no OpenJDK 7.

Abaixo está a descrição do erro,

Descrição do Bug:

Ao invocar um método com as seguintes assinaturas sobrecarregadas, espero um erro de ambiguidade (supondo que os argumentos sejam compatíveis com ambos):

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

O javac trata o segundo como mais específico que o primeiro. Esse comportamento é sensato (eu prefiro), mas é inconsistente com o JLS (15.12.2).

de JLS 15.12.2.2

JLS 15.12.2.2 Escolha o método mais específico

Se mais de uma declaração de método é acessível e aplicável a uma invocação de método, é necessário escolher uma para fornecer o descritor para o despacho do método de tempo de execução. A linguagem de programação Java usa a regra de que o método mais específico é escolhido. A intuição informal é que uma declaração de método é mais específica que outra se qualquer chamada tratada pelo primeiro método puder ser passada para a outra sem um erro de tipo em tempo de compilation.

nenhum desses methods pode ser passado para o outro (os tipos de int [] e Integer [] são relacionados), portanto, a chamada é ambígua

O compilador não sabe qual método deve ser chamado. Para corrigir isso, você precisa converter os parâmetros de input.

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

EDITAR:

É porque o compilador tenta converter o Integer em int. Portanto, uma conversão implícita ocorre antes da invocação do método de call . Portanto, o compilador procura por qualquer método com esse nome que possa levar ints. E você tem 2 deles, então o compilador não sabe qual dos dois deve ser chamado.

Se mais de um método puder ser aplicado, do que na especificação de linguagem Java que escolhemos o método mais específico , o parágrafo 15.12.2.5 :

Um método de membro de aridade variável chamado m é mais específico do que outro método de membro de aridade de variável com o mesmo nome se ( <: means subtyping ):

  1. Um método de membro tem n parâmetros e o outro tem parâmetros k, onde n ≥ k e:
    • Os tipos dos parâmetros do primeiro método de membro são T1, ..., Tn-1, Tn []. ( temos apenas um T_n [], que é Integer [], n = 1 )
    • Os tipos dos parâmetros do outro método são U1, ..., Uk-1, Uk []. ( novamente apenas um paramentador, que é int [], k = 1 )
    • Se o segundo método é genérico então deixe R1 ... Rp (p ≥ 1) ser seus parâmetros de tipo, seja Bl o limite declarado de Rl (1 ≤ l ≤ p), seja A1 ... Ap seja o tipo argumentos inferidos (§15.12.2.7) para esta invocação sob as restrições iniciais Ti << Ui (1 ≤ i ≤ k-1) e Ti << Uk (k ≤ i ≤ n), e seja Si = Ui [R1 = A1 ,. .., Rp = Ap] (1 ≤ i ≤ k). ( método não é genérico )
    • Caso contrário, deixe Si = Ui (1 ≤ i ≤ k). ( S1 = int [] )
    • Para todo j de 1 a k-1, Tj <: Sj, e ( nada aqui )
    • Para todo j de k para n, Tj <: Sk, e, ( Compare T1 <: S1, Integer [] <: int [] )
    • Se o segundo método é um método genérico como descrito acima, então Al <: Bl [R1 = A1, ..., Rp = Ap] (1 ≤ l ≤ p). ( método não é genérico )

Embora int primitivo é autoboxed para invólucro Integer , int[] não é autoboxed para Integer[] , que a primeira condição não mantém.

Segunda condição é quase a mesma.

Há também outras condições que não são válidas e, em seguida, devido ao JLS:

Dizemos que a invocação do método é ambígua e ocorre um erro em tempo de compilation.

Esta pergunta já foi feita várias vezes. A parte complicada é que f(1, 2, 3) está claramente passando int , então por que o compilador não pode escolher a versão f(int...) ? A resposta deve estar em algum lugar no JLS , que eu estou coçando a cabeça contra

De acordo com o §15.12.2.4, ambos os methods são aplicáveis ​​ao método de variável-aridade , então o próximo passo é identificar o mais específico.

Desafortunadamente, o §15.12.2.5 usa o teste de subtipo T i <: S i entre os parâmetros formais f1 (T 1 , .. T n ) e f2 (S 1 , .. S n ) para identificar o método alvo, e como existe nenhuma relação de subtipo entre Integer e int , ninguém ganha , porque nem int:> Integer nem Integer:> int . No final do parágrafo é indicado:

As condições acima são as únicas circunstâncias em que um método pode ser mais específico que outro. […]

Um método m1 é estritamente mais específico que outro método m2 se e somente se m1 é mais específico que m2 e m2 não é mais específico que m1.

Diz-se que um método é maximamente específico para uma invocação de método se for acessível e aplicável e não houver outro método que seja aplicável e acessível e que seja estritamente mais específico.

É possível que nenhum método seja o mais específico, porque existem dois ou mais methods que são maximamente específicos. Nesse caso:

  1. […]

  2. Caso contrário, dizemos que a invocação do método é ambígua e ocorre um erro em tempo de compilation.

Anexou uma postagem no blog de Gilad Bracha (veja o Documento 2), por sua vez, ligada no relatório de bugs da resposta do @ Jayamhona.

Intereting Posts