2010-05-08 21 views
16

¿Alguien puede explicar en detalle el motivo por el que se invoca el método print(Parent parent) sobrecargado cuando se trabaja con la instancia Child en el fragmento de código de prueba?sobrecarga de método Java + envío doble

¿Alguna de las peculiaridades de los métodos virtuales o la sobrecarga/resolución de métodos en Java involucrados aquí? ¿Alguna referencia directa a Java Lang Spec? ¿Qué término describe este comportamiento? Muchas gracias.

public class InheritancePlay { 

    public static class Parent {   
     public void doJob(Worker worker) { 
      System.out.println("this is " + this.getClass().getName()); 

      worker.print(this); 
     } 
    } 

    public static class Child extends Parent { 
    } 

    public static class Worker { 
     public void print(Parent parent) { 
      System.out.println("Why this method resolution happens?"); 
     } 

     public void print(Child child) { 
      System.out.println("This is not called"); 
     } 
    } 

    public static void main(String[] args) { 
     Child child = new Child(); 
     Worker worker = new Worker(); 

     child.doJob(worker); 
    } 
} 

Respuesta

23

Los estados JLS en §8.4.9 Overloading:

  1. Cuando se invoca un método (§15.12), el número de argumentos reales (y cualquier argumentos de tipo explícitas) y los tipos de tiempo de compilación de los argumentos están utilizado, en tiempo de compilación, para determinar la firma del método que se invocará (§15.12.2).
  2. Si el método que se va a invocar es un método de instancia, el método real que se invocará se determinará en tiempo de ejecución, utilizando la búsqueda dinámica de métodos (§15.12.4).

Así, en su caso:

  1. El argumento de un método (this) es de tipo en tiempo de compilación Parent, y así se invoca el método print(Parent).
  2. Si se subclasó la clase Worker y la subclase anularía ese método, y la instancia worker pertenecía a esa subclase, se invocaría el método reemplazado.

Double dispatch no existe en Java. Tienes que simularlo, p. usando el Visitor Pattern. En este patrón, básicamente, cada subclase implementa un método accept y llama al visitante con this como argumento, y this tiene como tipo de tiempo de compilación esa subclase, por lo que se utiliza la sobrecarga de método deseada.

+0

Christian, gracias por la respuesta exhaustiva! Así que tratamos aquí con tipos de tiempo de compilación en tiempo de ejecución. Voy a profundizar mucho en ese tema. (El doble despacho se menciona aquí porque me encontré con esta pregunta aprendiendo el patrón de visitante :)). Max – Max

5

La razón es que doJob se implementa en Parent y no sobrecargada en Child. Pasa this al print methos del trabajador, porque this es del tipo Parent se llamará al método Worker::print(Parent).

el fin de tener Worker::print(Parent) que llama needto sobrecarga doJob en Child:

public static class Child extends Parent { 
    public void doJob(Worker worker) { 
     System.out.println("from Child: this is " + this.getClass().getName()); 

     worker.print(this); 
    } 
} 

En el código anterior this.getClass() en Child es equivalente a Child.class.

+0

rsp, gracias por señalar esto, pero sé que anulando doJob en Child hace que el trabajo progrese. Lo que pasa es que no entiendo por qué :). Echemos un vistazo al código inicial. Cuando pasamos el objeto Child a Parent.doJob La reflexión de Java dentro de Parent.doJob demuestra que tratamos con esto de tipo Child, entonces ¿por qué falla la resolución del método sobrecargado? – Max

+1

@Max, dentro de un método, el tipo de 'this' es siempre el tipo de la clase en la que se encuentra el método.El compilador no puede saber que la clase se heredará en el futuro, tiene que usar el conocimiento que tiene en ese momento. 'this.getClass()' es información de tiempo de ejecución, no de compilación. – rsp

+0

rsp, muchas gracias. ya lo tengo. – Max