我正在为SCJP做准备(最近被Oracle重新命名为OCPJP),我在模拟考试中遇到的一个特殊问题让我感到困惑,答案描述并不能解释清楚的事情。
这就是问题:
class A
{
int x = 5;
}
class B extends A
{
int x = 6;
}
public class CovariantTest
{
public A getObject()
{
return new A();
}
public static void main(String[]args)
{
CovariantTest c1 = new SubCovariantTest();
System.out.println(c1.getObject().x);
}
}
class SubCovariantTest extends CovariantTest
{
public B getObject()
{
return new B();
}
}
答案是5
,但我选择6
。
我理解覆盖适用于运行时的方法,而不是变量,但我的思维解释println
的方式是:
SubCovariantTest
对象,并且具有有效覆盖
对于getObject()
,请使用重写方法是JVM忽略getObject()
部分的情况,并且始终从x
获取c1
,因为变量在编译时关联了吗?
答案 0 :(得分:12)
虽然为SubCovariantTest正确完成了覆盖,但答案是5,因为声明了变量c1的方式。它被声明为CovariantTest,而不是SubCovariantTest。
当运行c1.getObject()。x时,它不知道它是SubCovariantTest(没有使用转换)。这就是为什么5从CovariantTest返回而不是6从SubCovariantTest返回。
如果你改变了
System.out.println(c1.getObject().x);
到
System.out.println(((SubCovariantTest) c1).getObject().x);
你会得到预期的6分。
编辑:正如评论中指出的那样
“字段在Java中不是多态的。只有方法是。子类中的x隐藏了基类中的x。它不会覆盖它。” (感谢JB Nizet)
答案 1 :(得分:4)
这里发生的事情的技术术语是“隐藏”。 Java中的变量名称由引用类型解析,而不是它们引用的对象。
但是,具有相同签名的实例方法被“覆盖”而不是“隐藏”,并且您无法访问从外部覆盖的方法版本。
请注意,隐藏也适用于具有相同签名的静态方法。
您的模拟问题以简化形式(没有覆盖):
class A {
int x = 5;
}
class B extends A {
int x = 6;
}
public class CovariantTest {
public static void main(String[] args) {
A a = new B();
B b = new B();
System.out.println(a.x); // prints 5
System.out.println(b.x); // prints 6
}
}
答案 2 :(得分:4)
好的,我知道回答这个问题有点迟了但是我和我的朋友有同样的问题,而且这里的答案对我们来说并不是很清楚。所以我现在只说明我遇到了什么问题以及它有多大意义:)
现在我明白那些领域不会被覆盖,但是他们会像miller.bartek所指出的那样被隐藏起来,而且我也明白,最重要的是方法而不是斯科特指出的领域。
我遇到的问题是这个。据我说,
c1.getObject().x
这必须转变为:
new B().x // and not newA().x since getObject() gets overrided
评估为6。
我无法得到为什么类A(超类)的变量被B类(子类)的对象调用而没有明确要求这样的行为。
从问题的措辞中猜测,我觉得OP有同样的问题/怀疑。
你得到了Elbek答案的暗示。将以下行放在main方法中并尝试编译代码:
A a = c1.getObject(); //line 1
B b = c1.getObject(); //line 2
您会注意到第1行完全合法,而第2行则出现编译错误。
因此,当调用函数getObject()时,CovariantTest(超级)函数被SubCovariantTest(sub)函数覆盖,因为它在代码中是有效的覆盖,而c1.getObject()将返回新的B()。
但是,由于超函数返回类类型A的引用,即使在被覆盖之后,必须返回类类型A的引用,除非我们对其进行类型转换。在这里,B类是 A类(由于继承)。
所以实际上,我们从c1.getObject()获得的不是
new B()
但是这个:
(A) new B()
这就是为什么输出结果为5,即使返回B类的对象而B类的值为x也为6。
答案 3 :(得分:3)
您正在调用c1
:System.out.println(c1.getObject().x);
c1参考类型是:
public class CovariantTest
{
public A getObject()
{
return new A();
}
public static void main(String[]args)
{
CovariantTest c1 = new SubCovariantTest();
System.out.println(c1.getObject().x);
}
}
因此:c1.getObject()
返回类型为A
。从A
你直接获得属性而不是方法,因为你提到java不会覆盖属性,所以它从x
抓取A
答案 4 :(得分:0)
当重写方法时,调用子类方法,并且当重写变量时,使用超类变量
答案 5 :(得分:0)
当子类和父类都有一个具有相同名称的变量时,子类的变量会隐藏父类的变量,这称为变量隐藏。
虽然变量隐藏看起来像覆盖一个类似于方法重写的变量,但事实并非如此,Overriding仅适用于隐藏的方法是适用的变量。
在方法重写的情况下,重写的方法完全取代了继承的方法,因此当我们尝试通过保存子对象从父对象的引用访问该方法时,将调用子类中的方法。 / p>
但是在变量隐藏中,子类隐藏了继承的变量而不是替换,因此当我们尝试通过保存子对象来从父对象的引用中访问变量时,它将从父类访问。
当子类中的实例变量与超类中的实例变量同名时,则从引用类型中选择实例变量。
上阅读更多内容