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 una manera de modificar el código para que se va a imprimir foo(12)
, foo("foobar")
y foo(Object o)
?
foo(Object)
. En tiempo de ejecución, la clase del objeto en el que se llama al método determina qué implementación de ese método se llama, teniendo en cuenta que puede ser una instancia de una subclase del tipo declarado que anula el método.Como se mencionó anteriormente, la resolución de sobrecarga se realiza en tiempo de compilación.
Java Puzzlers tiene un buen ejemplo de 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?
Solución 46: Caso del constructor confuso
... El proceso de resolución de sobrecargas 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 (doble []) , por lo que Confusing (Object) es menos específico. (Cada matriz doble es un Objeto , pero no todo Objeto es una matriz doble ). Por lo tanto, el constructor más específico es Confuso (doble []) , que explica la salida del programa.
Este comportamiento tiene sentido si pasa un valor de tipo double [] ; es contrario a la intuición si pasa null . La clave para entender este acertijo es que la prueba para qué método o constructor es más específico no usa los parámetros reales : los parámetros que aparecen en la invocación. Se utilizan solo 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 solo 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 nuevo Confusing ((Object) null) . Esto asegura que solo se aplique Confuso (Objeto) . De manera más general, para obligar al compilador a seleccionar una sobrecarga específica, convierta los parámetros reales en los tipos declarados de los parámetros formales.
fuente
La capacidad de enviar una llamada a un método basado en tipos de argumentos se denomina envío múltiple . En Java, esto se hace con el patrón Visitor .
Sin embargo, dado que está tratando con
Integer
syString
s, no puede incorporar fácilmente este patrón (simplemente no puede modificar estas clases). Por lo tanto, un giganteswitch
en tiempo de ejecución del objeto será su arma preferida.fuente
En Java, el método a llamar (como en qué firma de método usar) se determina en tiempo de compilación, por lo que va con el tipo de tiempo de compilación.
El patrón típico para solucionar esto es verificar el tipo de objeto en el método con la firma del objeto y delegar en el método con una conversión.
Si tiene muchos tipos y esto es inmanejable, entonces la sobrecarga de métodos probablemente no sea el enfoque correcto, sino que el método público debería simplemente tomar Object e implementar algún tipo de patrón de estrategia para delegar el manejo apropiado por tipo de objeto.
fuente
Tuve un problema similar al llamar al constructor correcto de una clase llamada "Parameter" que podría tomar varios tipos básicos de Java como String, Integer, Boolean, Long, etc. Dada una matriz de objetos, quiero convertirlos en una matriz de mis objetos Parameter llamando al constructor más específico para cada Objeto en la matriz de entrada. También quería definir el parámetro del constructor (Object o) que arrojaría una IllegalArgumentException. Por supuesto, encontré que este método se invoca para cada Objeto en mi matriz.
La solución que utilicé fue buscar el constructor a través de la reflexión ...
¡No se requieren instancias desagradables, declaraciones de cambio o patrones de visitantes! :)
fuente
Java mira el tipo de referencia cuando intenta determinar a qué método llamar. Si desea forzar su código, elija el método 'correcto', puede declarar sus campos como instancias del tipo específico:
También puede convertir sus parámetros como el tipo de parámetro:
fuente
Si hay una coincidencia exacta entre el número y los tipos de argumentos especificados en la llamada al método y la firma del método de un método sobrecargado, ese es el método que se invocará. Está utilizando referencias de objeto, por lo que Java decide en tiempo de compilación que para Object param, hay un método que acepta directamente Object. Así que llamó a ese método 3 veces.
fuente