java - ¿error con varargs y sobrecarga?
overloading variadic-functions (3)
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 docs dicen: "En términos generales, no debes sobrecargar un método varargs, o será difícil para los programadores descubrir qué sobrecarga se llama".
sin embargo, no mencionan este error, y no son los programadores quienes lo encuentran difícil, es el compilador.
pensamientos?
EDITAR - Compilador: Sun jdk 1.6.0 u18
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 compilador 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 bugs.sun.com/bugdatabase/view_bug.do?bug_id=6199075 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).
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 compilador.
Al menos, es una buena (?) SCJP truco preguntas.
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 genéricos:
public static <T> void doit(T... ts) {
for(T currT : ts) {
System.out.println(currT);
}
}