Java no anula los campos (también conocidos como atributos o variables de miembro). En su lugar, se shadow over entre sí. Si ejecuta el programa a través del depurador, encontrará dos variables x
en cualquier objeto que sea del tipo B
.
Aquí hay una explicación de lo que está sucediendo. El programa primero recupera algo que es implícitamente del tipo A
y luego llama al x
que se supone que proviene de A
. Aunque es claramente un subtipo, en su ejemplo se crea un objeto de tipo B
a través de SubCovariantTest
, aún asume que debe devolver algo en getObj() que está implícitamente escrito A. Dado que Java no puede anular campos, la prueba llamará al A.x
y no B.x
.
CovariantTest c = new SubCovariantTest();
// c is assumed the type of CovariantTest as it is
// implicitly declared
System.out.println(c.getObj().x);
// In this method chain the following happens:
// c.getObj() will return object of type B
// BUT will assume it is an A
// c.getObj().x will return the x from A
// since in this context the compiler assumes
// it is an A and make the call to A.x
Parece una Gotcha mindboggling porque los métodos siempre se redefinen en Java (en comparación con C++ y C# en los que no lo son). Por lo general, no se encontrará con este problema porque la convención del código Java le indica que nunca debe acceder directamente a los campos. En su lugar, asegúrese de que los campos siempre se accede a través de métodos de acceso, es decir captadores:
class A {
private int x = 5;
public int getX() { // <-- This is a typical accessor method
return x;
}
}
class B extends A {
private int x = 6;
@override
public int getX() {
// will be called instead even though B is implied to be A
// @override is optional because methods in Java are always virtual
// thus are always overridden
return x;
}
}
código para conseguir este trabajo es la siguiente:
c.getObj().getX();
// Will now call getX() in B and return the x that is defined in B's context.