Java doesn't override fields (aka. attributes or member variables). Instead they shadow over each other. If you run the program through the debugger, you'll find two x
variables in any object that is of type B
.
Here is an explanation on what is happening. The program first retrieves something that is implicitly of type A
and then call for the x
that is assumed to come from A
. Even though it is clearly a subtype, in your example an object of type B
is created through SubCovariantTest
, it still assumes you return something in getObj() that is implicitly typed A. Since Java cannot override fields, the test will call A.x
and not 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
It seems like a mindboggling gotcha because methods are always overridden in Java (in comparison to C++ and C# in which they are not). You usually won't run into this problem because Java code convention tells you to never access fields directly. Instead make sure that fields are always accessed through accessor methods, i.e. getters:
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;
}
}
Code to get this working is the following:
c.getObj().getX();
// Will now call getX() in B and return the x that is defined in B's context.