Con esta pregunta, el examen SCJP evalúa su conocimiento de lo que se conoce como que oculta. El examinador complicó deliberadamente las cosas para tratar de hacerle creer que el comportamiento del programa depende solo del polimorfismo, lo cual no es cierto.
Tratemos de aclarar un poco las cosas al eliminar el método addFive()
.
class Foo {
public int a = 3;
}
class Bar extends Foo {
public int a = 8;
}
public class TestClass {
public static void main(String[]args) {
Foo f = new Bar();
System.out.println(f.a);
}
}
Ahora las cosas son un poco menos confusas.El método main
declara una variable de tipo Foo
, a la que se le asigna un objeto de tipo Bar
en tiempo de ejecución. Esto es posible ya que Bar
hereda de Foo
. El programa se refiere al campo público a
de la variable de tipo Foo
.
El error aquí sería creer que el mismo tipo de concepto conocido como anulando se aplica a los campos de clase. Pero no hay tal concepto para los campos: el campo público a
de clase Bar
no primordial es el campo público a
de clase Foo
pero sí lo que se llama ocultar. Como su nombre lo indica, significa que en el ámbito de la clase Bar
, a
se referirá al campo propio de Bar
que no tiene nada que ver con el de Foo
. (JLS 8.4.8 - Inheritance, Overriding, and Hiding)
Entonces, cuando estamos escribiendo f.a
, ¿a qué a
nos referimos? Recuerde que la resolución del campo a
se realiza en compilando el tiempo usando el tipo de declaración del objeto f
, que es Foo
. Como consecuencia, el programa imprime '3'.
Ahora, agreguemos un método addFive()
en la clase Foo
y anule en la clase Bar
como en la pregunta del examen. Aquí se aplica el polimorfismo, por lo tanto, la llamada f.addFive()
se resuelve utilizando no el tiempo de compilación, pero el tipo de tiempo de ejecución del objeto f
, que es Bar
, y por lo tanto se imprime 'b'.
Pero todavía hay algo que debemos entender: ¿por qué el campo a
, que se incrementó en 5 unidades, todavía se adhiere al valor '3'? Aquí ocultando está jugando. Debido a que este es el método de la clase Bar
que se llama, y porque en la clase Bar
, cada a
se refiere al campo público Bar
a
, este es realmente el campo Bar
que se incrementa.
1) Ahora, una pregunta subsidiaria: cómo podríamos acceder ámbito público de la Bar
's a
del método main
? Podemos hacer eso con algo como:
System.out.println(((Bar)f).a);
que fuerzan el compilador para resolver el miembro campo a
de f
como Bar
's a
campo.
Esto imprimiría 'b 13' en nuestro ejemplo.
2) Sin embargo, otra pregunta: ¿Cómo podríamos evitar escondido en addFive()
método de la clase Bar
para referirse no a a
campo de la Bar
's, pero a su campo eponimous superclase?Simplemente añadiendo la palabra clave super
frente a la referencia de campo hace el truco:
public void addFive() {
super.a += 5;
System.out.print("b ");
}
Esto imprimiría 'b 8' en nuestro ejemplo.
Tenga en cuenta que la declaración inicial
public void addFive() {
this.a += 5;
System.out.print("b ");
}
podría ser refinado para
porque cuando el compilador está resolviendo el campo a
, comenzará a buscar en el ámbito de inclusión más cercano dentro de la método addFive()
, y encuentre la instancia de clase Bar
, eliminando la necesidad de utilizar explícitamente this
.
Pero, bueno, this
fue probablemente una pista para que el examinado resolviera esta pregunta del examen.
Esta es una pregunta con respecto a SCJP, ¿verdad? –
yep @PrakharMohan ¿también has aparecido por lo mismo? todavía no lo he ... :) –