问题
public class CovariantTest {
public A getObj() {
return new A();
}
public static void main(String[] args) {
CovariantTest c = new SubCovariantTest();
System.out.println(c.getObj().x);
}
}
class SubCovariantTest extends CovariantTest {
public B getObj() {
return new B();
}
}
class A {
int x = 5;
}
class B extends A {
int x = 6;
}
The above code prints 5 when compiled and run. It uses the covariant return for the over-ridden method.
Why does it prints 5 instead of 6, as it executes the over ridden method getObj in class SubCovariantTest.
Can some one throw some light on this. Thanks.
回答1:
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.
回答2:
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() { //
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.
回答3:
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 ;-)
回答4:
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.
回答5:
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.
回答6:
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.
回答7:
c is typed as CovariantTest at compile time and thus the call to c.getObj() is bound to the method CovariantTest.getObj() at compile time (and that can't be modified at runtime).
Also, x exists in both A and B (it is shadowed, not overridden). Because the method being called is CovariantTest.getObj() and that method works with A, the x being retrieved is A.x even though the actual object is of type B.
来源:https://stackoverflow.com/questions/404804/java-covariants