public class CovariantTest {
public A getObj() {
return new A();
}
public static void main(String[] args) {
CovariantTest c = new SubCov
package ch2;
class CovariantTest
{
public A getObj()
{
return new A();
}
}
class SubCovariantTest extends CovariantTest
{
public B getObj()
{
return new B();
}
}
public class TestPrg
{
public static void main(String[] args)
{
CovariantTest c = new SubCovariantTest();
System.out.println("c.getObj().x :: "+c.getObj().x);
System.out.println("c.getObj().getX() :: "+c.getObj().getX());
}
}
class A
{
int x = 5;
int getX()
{
return x;
}
}
class B extends A
{
int x = 6;
int getX()
{
return x;
}
}
Simple... Polymorphism is only applicable for functions. Not variables.
Variables will be resolved during compile time.
Replace your A and B above with:
class A {
public int getX() { return 5; }
}
class B extends A {
public int getX() { return 6; }
}
That will probably answer your question about what is wrong ;-)
This is because in Java member variables don't override, they shadow (unlike methods). Both A and B have a variable x. Since c is declared to be of type CovarientTest, the return of getObj() is implicitly an A, not B, so you get A's x, not B's x.
There are two fields named x in the object, one from class A and one from class B, which hides the one in A. The field x referred to is the one in A, because of the declaration of c.
In practice this is not a problem, because it is very bad style to
hide a field in a subclass,
access a field directly instead of via a method.
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.
I was looking at your code and was having some trouble compiling it. Getting error
The return type is incompatible with CovariantTest.getObj()
I made a small change.
class A {
int x = 5;
}
class B extends A {
int x = 6;
}
public class CovariantTest {
public A getObj() {
return new A();
}
public static void main(String[] args) {
CovariantTest c = new SubCovariantTest();
A a = c.getObj();
System.out.println(c.getObj().x);
}
}
class SubCovariantTest extends CovariantTest {
public A getObj() {
return new B();
}
}
Stick a breakpoint in at the system out line and look at the a variable. It contains two x members, with one set to 5 and one to 6.
Starblue's answer explains this behaviour.