bug com varargs e sobrecarga?

Parece haver um bug na implementação do Java varargs. Java não consegue distinguir o tipo apropriado quando um método é sobrecarregado com diferentes tipos de parâmetros vararg.

Isso me dá um erro The method ... is ambiguous for the type ...

Considere o seguinte código:

 public class Test { public static void main(String[] args) throws Throwable { doit(new int[]{1, 2}); // <- no problem doit(new double[]{1.2, 2.2}); // <- no problem doit(1.2f, 2.2f); // <- no problem doit(1.2d, 2.2d); // <- no problem doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test } public static void doit(double... ds) { System.out.println("doubles"); } public static void doit(int... is) { System.out.println("ints"); } } 

os documentos dizem: “De um modo geral, você não deve sobrecarregar um método varargs, ou será difícil para os programadores descobrirem qual sobrecarga é chamada.”

no entanto, eles não mencionam esse erro, e não são os programadores que estão achando difícil, é o compilador.

pensamentos?

EDIT – Compiler: Sun jdk 1.6.0 u18

Há uma discussão sobre isso nos fóruns da Sun.

Nenhuma resolução real lá, apenas resignação.

O Varargs (e o auto-boxing, que também leva a um comportamento difícil de seguir, especialmente em combinação com varargs) foram corrigidos mais tarde na vida de Java, e essa é uma área em que é mostrada. Então é mais um bug na especificação do que no compilador.

Pelo menos, faz boas (?) Questões de truque SCJP.

O problema é que é ambíguo.

 doIt(1, 2); 

poderia ser uma chamada para doIt(int ...) ou doIt(double ...) . Neste último caso, os literais inteiros serão promovidos para valores double .

Tenho certeza de que a especificação Java diz que esta é uma construção ambígua, e o compilador está apenas seguindo as regras estabelecidas pela especificação. (Eu teria que pesquisar isso ainda mais para ter certeza.)

EDIT – a parte relevante do JLS é ” 15.12.2.5 Escolhendo o Método Mais Específico “, mas está doendo minha cabeça.

Eu acho que o raciocínio seria aquele void doIt(int[]) não é mais específico (ou vice versa) do que void doIt(double[]) porque int[] não é um subtipo de double[] (e vice versa). Como as duas sobrecargas são igualmente específicas, a chamada é ambígua.

Por contraste, void doItAgain(int) é mais específico que void doItAgain(double) porque int é um subtipo de double acordo com o JLS. Portanto, uma chamada para doItAgain(42) não é ambígua.

EDIT 2 – @finnw está certo, é um bug. Considere esta parte de 15.12.2.5 (editada para remover casos não aplicáveis):

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:

Um método de membro tem n parâmetros e o outro tem parâmetros k, onde n ≥ k. Os tipos dos parâmetros do primeiro método de membro são T1,. . . , Tn-1, Tn [], os tipos dos parâmetros do outro método são U1,. . . Uk-1, Uk []. Seja Si = Ui, 1 < = i <= k. Então:

  • para todo j de 1 a k-1, Tj <: Sj, e,
  • para todo j de k para n, Tj <: Sk

Aplique isto ao caso em que n = k = 1, e vemos que doIt(int[]) é mais específico que doIt(double[]) .


Na verdade, há um relatório de bug para isso e a Sun reconhece que é realmente um bug, embora tenha priorizado isso como “muito baixo” . O bug agora está marcado como Corrigido no Java 7 (b123).

Interessante. Felizmente, existem algumas maneiras diferentes de evitar esse problema:

Você pode usar os tipos de wrapper nas assinaturas do método:

  public static void doit(Double... ds) { for(Double currD : ds) { System.out.println(currD); } } public static void doit(Integer... is) { for(Integer currI : is) { System.out.println(currI); } } 

Ou você pode usar genéricos:

  public static  void doit(T... ts) { for(T currT : ts) { System.out.println(currT); } }