Pregunta

Ayer tuve una entrevista técnica telefónica de dos horas (que aprobé, ¡woohoo!), pero acepté completamente la siguiente pregunta sobre el enlace dinámico en Java. Y es doblemente desconcertante porque solía enseñar este concepto a estudiantes universitarios cuando era un TA hace unos años, por lo que la posibilidad de que les di información errónea es un poco inquietante ...

Aquí está el problema que me dieron:

/* What is the output of the following program? */

public class Test {

  public boolean equals( Test other ) {
    System.out.println( "Inside of Test.equals" );
    return false;
  }

  public static void main( String [] args ) {
    Object t1 = new Test();
    Object t2 = new Test();
    Test t3 = new Test();
    Object o1 = new Object();

    int count = 0;
    System.out.println( count++ );// prints 0
    t1.equals( t2 ) ;
    System.out.println( count++ );// prints 1
    t1.equals( t3 );
    System.out.println( count++ );// prints 2
    t3.equals( o1 );
    System.out.println( count++ );// prints 3
    t3.equals(t3);
    System.out.println( count++ );// prints 4
    t3.equals(t2);
  }
}

Afirmé que la salida debería haber sido dos declaraciones de impresión separadas dentro del método equals () anulado: en t1.equals (t3) y t3 .equals (t3) . El último caso es bastante obvio, y con el primer caso, aunque t1 tiene una referencia de tipo Object, se instancia como tipo Test, por lo que el enlace dinámico debería llamar a la forma anulada del método.

Aparentemente no. Mi entrevistador me animó a ejecutar el programa yo mismo, y he aquí que solo había una salida única del método anulado: en la línea t3.equals (t3) .

Mi pregunta es, ¿por qué? Como ya mencioné, aunque t1 es una referencia de tipo Object (por lo que el enlace estático invocaría el método equals () de Object), el enlace dinámico debería tenga cuidado de invocar la versión más específica del método basada en el tipo instanciado de la referencia. ¿Qué me estoy perdiendo?

¿Fue útil?

Solución

Java utiliza enlaces estáticos para métodos sobrecargados, y enlaces dinámicos para métodos anulados. En su ejemplo, el método equals está sobrecargado (tiene un tipo de parámetro diferente que Object.equals ()), por lo que el método llamado está vinculado al tipo reference en tiempo de compilación.

Alguna discusión aquí

El hecho de que sea el método de igualdad no es realmente relevante, aparte de que es un error común sobrecargarlo en lugar de anularlo, lo que ya conoce en función de su respuesta al problema en la entrevista.

Editar: Una buena descripción aquí también . Este ejemplo muestra un problema similar relacionado con el tipo de parámetro en su lugar, pero causado por el mismo problema.

Creo que si el enlace fuera realmente dinámico, cualquier caso en el que el llamador y el parámetro fueran una instancia de Prueba daría como resultado que se llamara al método anulado. Entonces t3.equals (o1) sería el único caso que no se imprimiría.

Otros consejos

El método equals de Test no anula el método equals de java.lang.Object . Mira el tipo de parámetro! La clase Test está sobrecargando igual con un método que acepta una Test .

Si el método igual a tiene la intención de anular, debe usar la anotación @Override. Esto provocaría un error de compilación para señalar este error común.

Curiosamente, en el código Groovy (que podría compilarse en un archivo de clase), todas las llamadas excepto una ejecutarían la declaración de impresión. (El que compara una Prueba con un Objeto claramente no llamará a la función Test.equals (Prueba)). Esto se debe a que groovy SÍ escribe completamente dinámico. Esto es particularmente interesante porque no tiene ninguna variable que se tipee de forma explícita y dinámica. He leído en un par de lugares que esto se considera dañino, ya que los programadores esperan que Groovy haga lo de Java.

Java no admite la covarianza en los parámetros, solo en los tipos de retorno.

En otras palabras, aunque su tipo de retorno en un método de anulación puede ser un subtipo de lo que era anulado, eso no es cierto para los parámetros.

Si su parámetro para iguales en Object es Object, poner un igual con cualquier otra cosa en una subclase será un método sobrecargado, no anulado. Por lo tanto, la única situación en la que se llamará a ese método es cuando el tipo estático del parámetro es Test, como en el caso de T3.

¡Buena suerte con el proceso de entrevista de trabajo! Me encantaría ser entrevistado en una empresa que hace este tipo de preguntas en lugar de las preguntas habituales de estructuras de datos / datos que les enseño a mis alumnos.

Creo que la clave está en el hecho de que el método equals () no se ajusta al estándar: toma otro objeto Test, no un objeto Object y, por lo tanto, no anula el método equals (). Esto significa que en realidad solo lo ha sobrecargado para hacer algo especial cuando se le da un objeto de prueba, mientras que el objeto Object llama Object.equals (Object o). Mirar ese código a través de cualquier IDE debería mostrarle dos métodos equals () para Test.

El método está sobrecargado en lugar de anulado. Iguales siempre toman un objeto como parámetro.

por cierto, tienes un elemento sobre esto en el java efectivo de Bloch (que deberías tener).

Algunas notas en Enlace dinámico (DD) y Enlace estático & # 803; & # 803; & # 803; (SB) después de buscar un momento:

1.Timing execute : (Ref.1)

  • DB: en tiempo de ejecución
  • SB: tiempo de compilación

2.Utilizado para :

  • DB: anulación
  • SB: sobrecarga (estática, privada, final) (Ref.2)

Referencia:

  1. Ejecutar resolución media qué método prefiere usar
  2. Porque no puede anular el método con un modificador estático, privado o final
  3. http: // javarevisited. blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

Si se agrega otro método que anule en lugar de sobrecargar, explicará la llamada de enlace dinámico en tiempo de ejecución.

/ * ¿Cuál es la salida del siguiente programa? * /

public class DynamicBinding {
    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside @override: this is dynamic binding");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++);// prints 0
        t1.equals(t2);
        System.out.println(count++);// prints 1
        t1.equals(t3);
        System.out.println(count++);// prints 2
        t3.equals(o1);
        System.out.println(count++);// prints 3
        t3.equals(t3);
        System.out.println(count++);// prints 4
        t3.equals(t2);
    }
}

Encontré un artículo interesante sobre enlace dinámico vs.enlace estático. Viene con un código para simular el enlace dinámico. Hizo que mi código fuera más legible.

https://sites.google.com/site/jeffhartkopf/covariance

Consulte también esta pregunta SO, estrechamente relacionada: Anulación de la peculiaridad del método de igualdad de JAVA

La respuesta a la pregunta "¿por qué?" así es como se define el lenguaje Java.

Para citar el Artículo de Wikipedia sobre covarianza y contravarianza :

  

Se implementa la covarianza de tipo de retorno   en el lenguaje de programación Java   versión J2SE 5.0. Los tipos de parámetros tienen   ser exactamente el mismo (invariante) para   anulación del método, de lo contrario el   El método está sobrecargado con un paralelo   definición en su lugar.

Otros idiomas son diferentes.

Está muy claro, que no hay un concepto de anulación aquí. Es sobrecarga de métodos. el método Object () de la clase Object toma el parámetro de referencia de tipo Object y este método equal () toma el parámetro de referencia de tipo Test.

Trataré de explicar esto a través de dos ejemplos, que son las versiones extendidas de algunos de los ejemplos que encontré en línea.

public class Test {

    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside of Test.equals ot type Object");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++); // prints 0
        o1.equals(t2);

        System.out.println("\n" + count++); // prints 1
        o1.equals(t3);

        System.out.println("\n" + count++);// prints 2
        t1.equals(t2);

        System.out.println("\n" + count++);// prints 3
        t1.equals(t3);

        System.out.println("\n" + count++);// prints 4
        t3.equals(o1);

        System.out.println("\n" + count++);// prints 5
        t3.equals(t3);

        System.out.println("\n" + count++);// prints 6
        t3.equals(t2);
    }
}

Aquí, para líneas con valores de conteo 0, 1, 2 y 3; tenemos referencia de Objeto para o1 y t1 en el método equals () . Por lo tanto, en tiempo de compilación, el método equals () del archivo Object.class estará limitado.

Sin embargo, aunque la referencia de t1 es Objeto , tiene intialización de Clase de prueba .
Object t1 = new Test (); .
Por lo tanto, en tiempo de ejecución llama a public boolean equals (Object other) que es un

  

método anulado

. ingrese la descripción de la imagen aquí

Ahora, para valores de conteo como 4 y 6, nuevamente es sencillo que t3 que tiene referencia y inicialización de Test está llamando a < code> equals () método con parámetro como referencias de objeto y es un

  

método sobrecargado

OK!

  

Nuevamente, para comprender mejor a qué método llamará el compilador, solo   haga clic en el método y Eclipse resaltará los métodos de similar   tipos que cree que llamarán en el momento de la compilación. Si no consigue   llamado en tiempo de compilación, entonces esos métodos son un ejemplo de método   anulación.

 ingrese la descripción de la imagen aquí

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