2011-05-15 25 views
15

estoy entrenando para un examen de Java, y he encontrado algo que no entiendo en el año último tema. Aquí está el códigoJava "truco", redefiniendo miembro de la clase hija

class Mother { 
    int var = 2; 

    int getVar() { 
     return var; 
    } 
} 

class Daughter extends Mother { 
    int var = 1; 

    int getVar() { 
     return var; 
    } 

    public static void main(String[] args) { 
     Mother m = new Mother(); 
     System.out.println(m.var); 
     System.out.println(m.getVar()); 
     m = new Daughter(); 
     System.out.println(m.var); 
     System.out.println(m.getVar()); 
    } 
} 

La pregunta es "¿cuál es la salida de este programa?". Me hubiera ir con 2 2 1 1, pero al compilar y ejecutar esta pieza de código, me sale 2 2 2 1.

Alguien me puede explicar por qué?

Gracias por leer!

+2

'Hija' extiende' Madre'? eso es bastante extraño, ya que en realidad es al revés. – mbx

+0

Estaría interesado en escuchar la razón también. Ejecuté esto en Eclipse y verifiqué los valores con el depurador, el depurador en realidad muestra la variable m local teniendo DOS var-members diferentes después de m = new Daugher() -line. m.var parece resolverse al de Madre (¿quizás porque la variable local está declarada como Madre, no estoy seguro?), y m.getVar() llama al getVar en Hija (como se esperaba). – esaj

+0

Tenga en cuenta que en los programas reales esto no sucedería, porque normalmente haría que 'var' sea privada. Y si alguna vez lo hace accesible desde fuera de la clase, lo que debería ser muy raro, entonces debe asegurarse de que las variables no se ocultan entre sí. – starblue

Respuesta

16

La llamada al método m.getVar() es una llamada a un método virtual. La segunda vez que lo llama, se distribuye dinámicamente al derivado Daughter.getVar(), que hace lo que espera (tiene acceso a Daugther.var y lo devuelve).

No existe tal mecanismo de despacho virtual para campos miembros. Entonces m.var siempre se refiere a Mother.var, es decir, la versión de la clase base de esa variable.

Se puede considerar que la clase Daughter tiene dos miembros diferentes var: uno de Mother y el suyo. Su propio miembro "oculta" el que está en Mother, pero se puede acceder desde dentro de la clase Daughter utilizando super.var.

La especificación oficial para esto está en la sección 8.3 Field Declarations del JLS. Cita:

Si la clase declara un campo con un cierto nombre, a continuación, la declaración de que el campo se dice que ocultar cualquiera y todas las declaraciones accesibles de los campos con el mismo nombre en superclases y superinterfaces de la clase. La declaración de campo también sombrea (§6.3.1) declaraciones de cualquier campo accesible en clases o interfaces adjuntas, y cualquier variable local, parámetros de método formal y parámetros de controlador de excepción con el mismo nombre en cualquier bloque envolvente.

Tenga en cuenta que puede ser bastante interesante (énfasis añadido):

Si una declaración de campo oculta la declaración de otro campo, los dos campos no necesita tener el mismo tipo.

Y:

Puede haber varios caminos por los que la misma declaración de campo podría ser heredada de una interfaz. En tal situación, el campo se considera que se hereda solo una vez, y se puede hacer referencia a él por su nombre simple sin ambigüedad.

modo que el párrafo está bien vale la pena leer :-)

+0

Entonces, si lo hago bien, cuando una clase derivada redefine un campo de miembro, el acceso directo a este campo devolverá el campo de clase padre, mientras que al acceder a él a través de un método devolverá el campo derecho. Es asi ? – ksol

+0

no exactamente. Depende de cómo está accediendo a él. Si se hace referencia a su objeto como hija, entonces accederá al campo de clase derivada – Heisenbug

+0

según el tipo de referencia que esté utilizando. Si hubiera usado 'Daughter d = new Daugther(); System.out.println (d.var); 'obviamente habría devuelto' 1'. – Mat

6

centrarse en estas líneas:

Mother m; 
m = new Daughter(); 
System.out.println(m.var); 
System.out.println(m.getVar()); 

Usted está construyendo un objeto de la hija, pero que lo están tratando como si fuera la clase base de la madre. Entonces cuando accedes a m.var está accediendo a la variable de la clase base var. Mientras tanto, cuando llamas a un método, incluso si te estás refiriendo a la referencia de la clase base, se llama al método anulado. Es un comportamiento diferente para los métodos y campos .. La referencia de campos no se puede sobrescribir.

1
m = new Daughter(); 

Aunque se ha creado un objeto daughter, se está refiriendo ese objeto con Mother m referencia. Por lo tanto, cualquier llamada usando m llamará a los miembros de la clase madre, no la hija

0

me encontré con esto en Eclipse y comprobado con los valores de depuración, el depurador muestra la realidad local de m -Variable que tienen dos diferentes -miembros var después de la m = new Daugher() - la línea con los valores 2 y 1. m.var parece resolverse a la de Madre, y m.getVar() llama al getVar en Hija (como se esperaba).

Sin embargo, cuando cambio el principal método para tener este aspecto:

Mother m = new Mother(); 
    System.out.println(m.var); 
    System.out.println(m.getVar()); 
    Daughter d = new Daughter(); 
    System.out.println(d.var); 
    System.out.println(d.getVar()); 

En realidad, salida de 2, 2, 1, 1, por lo que parece que la declaración de la variable afecta a qué clase de var es usado.

2

Los métodos se pueden anular, pero los campos solo se pueden ocultar. La diferencia es que un método no estático usa el tipo del objeto al que se hace referencia, un campo toma el tipo de la referencia. Usted ve algo similar con los métodos estáticos que solo se ocultan cuando se ignora la clase de la "referencia" y el objeto (si se proporciona).

Para su interés, intente dar a los campos diferentes tipos. ;)

También puede intentar

System.out.println(((Mother)m).var); // uses var in Mother 
System.out.println(((Daughter)m).var); // uses var in Daughter 
0

leí las respuestas y no de ellos (hasta ahora) dio una buena razón por la cual en el lenguaje orientado a objetos como Java es, este debería ser el caso. Voy a intentar de explicar.

Suponga que tiene función que toma la madre como arg:

void foo(Mother m) { 
    print(m.var); 
} 

Esta función (en realidad el compilador) tiene ni idea de si va a llamar con Mother, Daughtero con otro Dauther2 que doesn' t incluso tiene var variable declarada. Por eso, cuando la referencia es de tipo Madre, la referencia a una variable miembro debe estar vinculada (por el compilador) al miembro Mother. El similar se aplica a funcionar demasiado, por lo que las funciones están vinculadas a declaración de la madre de getVar(), pero no a Mother 's aplicación de getVar()

Por lo tanto, las variables de miembro siempre están vinculados (por el compilador) basado en la referencia. Otra forma de explicarlo: Si extrae var Mother 's (y hacer getVar() compilables de la madre), su segundo m.var (cuando m se refiere a la hija) voluntad no compilar siquiera tiene Daughter miembro var.

Espero haber sido claro.

Cuestiones relacionadas