¿error con varargs y sobrecarga?

Parece haber un error en la implementación de Java varargs. Java no puede distinguir el tipo apropiado cuando un método está sobrecargado con diferentes tipos de parámetros vararg.

Me da un error. The method ... is ambiguous for the type ...

Considera el siguiente 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"); } } 

los documentos dicen: “En términos generales, no debes sobrecargar un método varargs, o será difícil para los progtwigdores descubrir qué sobrecarga se llama”.

sin embargo, no mencionan este error, y no son los progtwigdores quienes lo encuentran difícil, es el comstackdor.

pensamientos?

EDITAR – Comstackdor: Sun jdk 1.6.0 u18

Hay una discusión sobre esto en los foros de Sun.

No hay una resolución real allí, solo resignación.

Varargs (y auto-boxing, que también conduce a un comportamiento difícil de seguir, especialmente en combinación con varargs) se han atornillado más adelante en la vida de Java, y esta es un área donde se muestra. Por lo tanto, es más un error en la especificación que en el comstackdor.

Al menos, es una buena (?) SCJP truco preguntas.

El problema es que es ambiguo.

 doIt(1, 2); 

podría ser una llamada a doIt(int ...) , o doIt(double ...) . En este último caso, los literales enteros serán promovidos a valores double .

Estoy bastante seguro de que la especificación de Java dice que se trata de una construcción ambigua, y el comstackdor solo sigue las reglas establecidas por la especificación. (Tendría que investigar esto más para estar seguro.)

EDITAR – la parte relevante del JLS es ” 15.12.2.5 Elegir el método más específico “, pero me está haciendo doler la cabeza.

Creo que el razonamiento sería que void doIt(int[]) no es más específico (o viceversa) que void doIt(double[]) porque int[] no es un subtipo de double[] (y viceversa). Como las dos sobrecargas son igualmente específicas, la llamada es ambigua.

Por el contrario, void doItAgain(int) es más específico que void doItAgain(double) porque int es un subtipo de double según JLS. Por lo tanto, un llamado a doItAgain(42) no es ambiguo.

EDIT 2 – @finnw tiene razón, es un error. Considere esta parte de 15.12.2.5 (editada para eliminar casos no aplicables):

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:

Un método miembro tiene n parámetros y el otro tiene parámetros k, donde n ≥ k. Los tipos de parámetros del primer método miembro son T1,. . . , Tn-1, Tn [], los tipos de los parámetros del otro método son U1,. . . , Uk-1, Uk []. Deje Si = Ui, 1 < = i <= k. Entonces:

  • para todos j de 1 a k-1, Tj <: Sj, y,
  • para todos j de k a n, Tj <: Sk

Aplique esto al caso donde n = k = 1, y vemos que doIt(int[]) es más específico que doIt(double[]) .


De hecho, hay un informe de error para esto y Sun reconoce que de hecho es un error, aunque lo han priorizado como “muy bajo” . El error ahora está marcado como Fixed en Java 7 (b123).

Interesante. Afortunadamente, hay un par de formas diferentes de evitar este problema:

Puede usar los tipos de envoltura en su lugar en las firmas de métodos:

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

O bien, puede usar generics:

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