这是因为在Java成员变量不覆盖时,它们会影响(与方法不同).A和B都有变量x.由于c被声明为CovarientTest类型,因此getObj()的返回隐式为A,而不是B,因此得到A的x,而不是B的x.
这是因为在Java成员变量不覆盖时,它们会影响(与方法不同).A和B都有变量x.由于c被声明为CovarientTest类型,因此getObj()的返回隐式为A,而不是B,因此得到A的x,而不是B的x.
Java 不会覆盖字段(aka.属性或成员变量).相反,他们互相遮蔽.如果您通过调试器运行程序,您将x
在任何类型的对象中找到两个变量B
.
这是对正在发生的事情的解释.程序首先检索隐式类型的东西,A
然后调用x
假定来自的东西A
.尽管它显然是一个子类型,但在您的示例中,类型的对象B
是通过创建的SubCovariantTest
,它仍然假设您在getObj()中返回隐式类型为A的内容.由于Java无法覆盖字段,因此测试将调用A.x
而不是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
它似乎是一个令人难以置信的陷阱,因为方法总是在Java中被覆盖(与C++和C#相比,它们不是).您通常不会遇到此问题,因为Java代码约定会告诉您永远不要直接访问字段.而是确保始终通过访问器方法(即getter)访问字段:
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.getObj().getX(); // Will now call getX() in B and return the x that is defined in B's context.
将上面的A和B替换为:
class A { public int getX() { return 5; } } class B extends A { public int getX() { return 6; } }
这可能会回答你关于错误的问题;-)
在对象中有两个名为x的字段,一个来自A类,一个来自B类,它们隐藏了A中的字段.由于c的声明,字段x引用的是A中的字段.
在实践中,这不是一个问题,因为它是非常糟糕的风格
隐藏子类中的字段,
直接访问字段而不是通过方法.