Selección de método sobrecargado según el tipo real del parámetro
Estoy experimentando con este código:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
Esto se imprime foo(Object o)
tres veces. Espero que la selección del método tenga en cuenta el tipo de parámetro real (no el declarado). ¿Me estoy perdiendo de algo? ¿ Hay alguna manera de modificar este código para que se imprima foo(12)
y foo("foobar")
?foo(Object o)
Espero que la selección del método tenga en cuenta el tipo de parámetro real (no el declarado). ¿Me estoy perdiendo de algo?
Sí. Tu expectativa está equivocada. En Java, el envío de métodos dinámicos ocurre solo para el objeto sobre el que se invoca el método, no para los tipos de parámetros de métodos sobrecargados.
Citando la especificación del lenguaje Java :
Cuando se invoca un método (§15.12), el número de argumentos reales (y cualquier argumento de tipo explícito) y los tipos de argumentos en tiempo de compilación se utilizan, en el momento de la compilación, para determinar la firma del método que se invocará ( §15.12.2). Si el método que se va a invocar es un método de instancia, el método real que se va a invocar se determinará en tiempo de ejecución, utilizando la búsqueda dinámica de métodos (§15.12.4).
Como se mencionó antes, la resolución de sobrecarga se realiza en tiempo de compilación.
Java Puzzlers tiene un buen ejemplo para eso:
Rompecabezas 46: El caso del constructor confuso
Este rompecabezas te presenta dos constructores confusos. El método principal invoca un constructor, pero ¿cuál? La salida del programa depende de la respuesta. ¿Qué imprime el programa o incluso es legal?
public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}
Solución 46: Caso del constructor confuso
... El proceso de resolución de sobrecarga de Java opera en dos fases. La primera fase selecciona todos los métodos o constructores que son accesibles y aplicables. La segunda fase selecciona el más específico de los métodos o constructores seleccionados en la primera fase. Un método o constructor es menos específico que otro si puede aceptar cualquier parámetro pasado al otro [JLS 15.12.2.5].
En nuestro programa, ambos constructores son accesibles y aplicables. El constructor Confusing(Object) acepta cualquier parámetro pasado a Confusing(double[]) , por lo que Confusing(Object) es menos específico. (Cada matriz doble es un Objeto , pero no todos los Objetos son una matriz doble ). Por lo tanto, el constructor más específico es Confusing(double[]) , que explica la salida del programa.
Este comportamiento tiene sentido si pasa un valor de tipo double[] ; es contradictorio si pasa null . La clave para entender este rompecabezas es que la prueba para saber qué método o constructor es más específico no utiliza los parámetros reales : los parámetros que aparecen en la invocación. Se utilizan únicamente para determinar qué sobrecargas son aplicables. Una vez que el compilador determina qué sobrecargas son aplicables y accesibles, selecciona la sobrecarga más específica, utilizando sólo los parámetros formales: los parámetros que aparecen en la declaración.
Para invocar el constructor Confusing(Object) con un parámetro nulo , escriba new Confusing((Object)null) . Esto garantiza que solo se aplique Confusing(Object) . De manera más general, para obligar al compilador a seleccionar una sobrecarga específica, convierta los parámetros reales a los tipos declarados de los parámetros formales.