2011-04-14 11 views
14

En el siguiente código, ¿cómo imprimen las declaraciones de impresión primera y segunda SubObj? ¿Subir y subpunto a la misma Subclase?Pregunta sobre la sobrecarga de Java y el enlace dinámico

class Top { 
    public String f(Object o) {return "Top";} 
} 

class Sub extends Top { 
    public String f(String s) {return "Sub";} 
    public String f(Object o) {return "SubObj";} 
} 

public class Test { 
    public static void main(String[] args) { 
     Sub sub = new Sub(); 
     Top top = sub; 
     String str = "Something"; 
     Object obj = str; 


     System.out.println(top.f(obj)); 
     System.out.println(top.f(str)); 
     System.out.println(sub.f(obj)); 
     System.out.println(sub.f(str)); 
    } 
} 

El código anterior arroja el resultado siguiente.

SubObj 
SubObj 
SubObj 
Sub 
+0

Ahora tengo cómo funciona la primera línea, pero ¿cómo es que la segunda línea imprime SubObj, incluso si el en put call era top.f (str) donde str es un tipo de String? – user482594

+0

He publicado una respuesta, ¿la miraste? Debería resolver tus dudas. Para resumir piense desde el punto de "Type Check" para el argumento pasado. Acepte la respuesta si lo encuentra útil. –

Respuesta

10

Desde ya entender el caso 1, 3 y 4, vamos a abordar el caso 2.

(Tenga en cuenta - No soy de ninguna manera un experto en el funcionamiento interno de la JVM o compiladores, pero esta es la forma en Lo entiendo. Si alguien que está leyendo esto es un experto en JVM, puede editar esta respuesta sobre cualquier discrepancia que pueda encontrar)

Un método en una subclase que tiene el mismo nombre pero una firma diferente se conoce como sobrecarga de método . La sobrecarga de métodos usa enlaces estáticos, lo que básicamente significa que el método apropiado será forzado a ser "elegido" (es decir, vinculado) en tiempo de compilación. El compilador no tiene idea sobre el tipo de tiempo de ejecución (también conocido como el tipo real) de sus objetos.Entonces cuando escribe:

      // Reference Type // Actual Type 
    Sub sub = new Sub(); // Sub    Sub 
    Top top = sub;  // Top    Sub 

el compilador solo "sabe" que la parte superior es de tipo Top (también conocido como el tipo de referencia). Así que cuando se escribe más adelante:

System.out.println(top.f(str)); // Prints "subobj" 

el compilador "ve" la llamada 'top.f' se refiere a un método f de la clase superior. "Sabe" que str es de tipo String que extiende Object. Entonces, desde 1) la llamada 'top.f' se refiere al método f de Top class, 2) no hay un método f en la clase Top que tome un parámetro String, y 3) porque str es una subclase de Object, el método f de Top class es la única opción válida en tiempo de compilación. Entonces, el compilador reenvía implícitamente str a su tipo principal, Object, para que pueda pasar al método f de Top. (Esto está en contraste con el enlace dinámico, donde la resolución de tipo de la línea de código anterior se aplazaría hasta el tiempo de ejecución, para ser resuelta por la JVM en lugar del compilador).

Luego, en tiempo de ejecución, en la línea de código anterior , la parte superior está bajada por la JVM a su tipo real, sub. Sin embargo, el compilador ha modificado el argumento str para que escriba Object. Entonces ahora la JVM tiene que llamar a un método f en el sub clase que toma un parámetro de tipo Objeto.

Por lo tanto, la línea de código anterior imprime "subobj" en lugar de "sub".

Para otro ejemplo muy similar, consulte: Java dynamic binding and method overriding

Actualización: Encontrado este artículo detallado sobre el funcionamiento interno de la JVM:

http://www.artima.com/underthehood/invocationP.html

os comentaba su código para que sea más clara ¿Qué está pasando:

class Top { 
    public String f(Object o) {return "Top";} 
} 

class Sub extends Top { 
    public String f(String s) {return "Sub";} // Overloading = No dynamic binding 
    public String f(Object o) {return "SubObj";} // Overriding = Dynamic binding 
} 

public class Test { 
    public static void main(String[] args) { 

            // Reference Type  Actual Type 
     Sub sub = new Sub();  // Sub    Sub 
     Top top = sub;   // Top    Sub 
     String str = "Something"; // String    String 
     Object obj = str;   // Object    String 

             // At Compile-Time:  At Run-Time: 
     // Dynamic Binding 
     System.out.println(top.f(obj)); // Top.f (Object) --> Sub.f (Object) 

     // Dynamic Binding 
     System.out.println(top.f(str)); // Top.f (Object) --> Sub.f (Object) 

     // Static Binding 
     System.out.println(sub.f(obj)); // Sub.f (Object)  Sub.f (Object) 

     // Static Binding 
     System.out.println(sub.f(str)); // Sub.f (String)  Sub.f (String) 
    } 
} 
1

Sí, ambos apuntan a Sub clase. La cuestión es que top sólo conoce acerca

f(Object o) 

y que sólo puede llamar a esa firma.

Pero sub conoce ambas firmas y tiene que seleccionar por tipo de parámetro.

6

Esto se debe a todas las llamadas a métodos en Java son virtual (por defecto).

Es decir, la resolución comienza a el objeto real (no el tipo de expresión) y "funciona hasta" la cadena de herencia (por los objetos reales tipo) hasta que se encuentra el primer método coincidente. Los métodos no virtuales comenzarían en el tipo de expresión . (Marcado de un método que hace que sea final no virtual.)

Sin embargo, la firma del método exacto se determina en tiempo de compilación (Java no soporta multi-despacho, de un solo despacho solamente varía en Run- tiempo basado en el objeto receptor): esto explica por qué Sub.f(String) da como resultado "Sub", por ejemplo, mientras Top.f(String) "se une" al método que coincide con Top.f(Object), incluso si se invoca sobre un subtipo de Superior. (Fue la mejor firma elegible determinada en tiempo de compilación). El despacho virtual en sí mismo es el mismo.

Happy coding.

2

Esto tiene que ver con el tipo aparente del objeto. En el momento de la compilación, Java realiza su verificación de tipos en función del tipo que declara que es su objeto en lugar del tipo específico que instancia.

Tiene un tipo Arriba con un método f (Objeto). Así, cuando decimos:

System.out.println(top.f(obj)); 

El compilador de Java sólo se preocupa de que la parte superior del objeto es de tipo superior y el único método disponible toma un objeto como un parámetro. En tiempo de ejecución, llama al método f (Objeto) del objeto instanciado real.

La siguiente llamada se interpreta de la misma manera.

Las siguientes dos llamadas se interpretan como era de esperar.

0

en

Sub sub = new Sub(); 
Top top = sub; 

se hizo una instancia de sub, y luego hasta fundido que al principio, lo que hace que sólo se sabe acerca de los métodos que existen en la parte superior. el método que existe en la parte superior es público String f(Object o) {return "Top";}

ahora también ese método está sobrecargado por el sub por lo que se llamará cuando se haga una instancia de sub y upcast hacia arriba.

otra forma de decir esto es que tienes

sub tipo que el tipo aparente, pero la parte superior como el tipo real, sub porque se ha asignado a la parte superior. llamará a los métodos en el tipo aparente si sobrecarga el tipo real, pero no podrá llamar a ningún método que no exista en el tipo real

1

En herencia, un objeto de clase base puede hacer referencia a una instancia de clase derivada.

Así es como Top top = sub; funciona bien.

  1. Para System.out.println(top.f(obj));:

    El objeto top intenta utilizar el método de la clase Subf(). Ahora que hay dos métodos f() en la clase Sub, la verificación de tipo se realiza para el argumento pasado. Como el tipo es Object, se invoca el segundo método f() de la clase Sub.

  2. Para System.out.println(top.f(str));:

    Se puede interpretar lo mismo que (1) es decir, el tipo es Stringf() lo que la primera función se invoca.

  3. Para System.out.println(sub.f(obj));:

    Esto es simple como la que está llamando el método de Sub clase misma. Ahora, dado que hay dos métodos sobrecargados en la clase Sub, aquí también se realiza la verificación de tipo para el argumento pasado. Como el argumento pasado es del tipo Object, se invoca el segundo método f().

  4. Para System.out.println(sub.f(str));:

    similares a 3., aquí el tipo pasado es Stringf() lo que la primera función de la clase Sub se invoca.

Espero que esto ayude.

+0

Para el segundo, la salida real es 'SubObj' no, 'Sub' – user482594

Cuestiones relacionadas