Pregunta

Hoy me encontré con una situación en la que Java no estaba invocando el método que esperaba: este es el caso de prueba mínimo: (lo siento, esto parece inventado - el escenario del "mundo real" es sustancialmente más complejo y hace que mucho más sentido desde un & "; ¿por qué demonios harías eso ? &"; punto de vista.)

Estoy específicamente interesado en por qué sucede esto, no me importan las sugerencias de rediseño. Tengo la sensación de que esto está en Java Puzzlers, pero no tengo mi copia a mano.

Vea la pregunta específica en los elogios dentro de Prueba < T > .getValue () a continuación:

public class Ol2 {  

    public static void main(String[] args) {  
        Test<Integer> t = new Test<Integer>() {  
            protected Integer value() { return 5; }  
        };  

        System.out.println(t.getValue());  
    }  
}  


abstract class Test<T> {  
    protected abstract T value();  

    public String getValue() {  
        // Why does this always invoke makeString(Object)?  
        // The type of value() is available at compile-time.
        return Util.makeString(value());  
    }  
}  

class Util {  
    public static String makeString(Integer i){  
        return "int: "+i;  
    }  
    public static String makeString(Object o){  
        return "obj: "+o;  
    }  
} 

El resultado de este código es:

obj: 5
¿Fue útil?

Solución

No, el tipo de valor no está disponible en tiempo de compilación. Tenga en cuenta que javac solo compilará una copia del código que se utilizará para todas las T posibles. Dado eso, el único tipo posible para que el compilador lo use en su método getValue () es Object.

C ++ es diferente, porque eventualmente creará múltiples versiones compiladas del código según sea necesario.

Otros consejos

Porque la decisión sobre qué makeString() usar se toma en tiempo de compilación y, en base al hecho de que T podría ser cualquier cosa, debe ser la versión Object. Piénsalo. Si hiciera Test<String> tendría que llamar a la versión Test<T>. Como tal, todas las instancias de makeString(Object) usarán <=>.

Ahora, si hiciste algo como:

public abstract class Test<T extends Integer> {
  ...
}

las cosas pueden ser diferentes.

El Java efectivo de Josh Bloch tiene una excelente discusión para aclarar la confusión que surge porque el despacho funciona de manera diferente para los métodos sobrecargados frente a los sobrescritos (en una subclase). La selección entre los métodos sobrecargados --- el tema de esta pregunta --- se determina en el momento de la compilación; La selección entre los métodos reemplazados se realiza en tiempo de ejecución (y, por lo tanto, obtiene conocimiento del tipo específico del objeto).

El libro es mucho más claro que mi comentario: ver " Artículo 41: Use la sobrecarga juiciosamente "

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top