Java 在涉及变量的情况下,关于重写的轻微混淆
我正在准备SCJP(最近被甲骨文更名为OCPJP),一个我在模拟考试中出错的问题让我困惑,答案描述没有足够清楚地解释问题 问题是:Java 在涉及变量的情况下,关于重写的轻微混淆,java,overriding,scjp,ocpjp,Java,Overriding,Scjp,Ocpjp,我正在准备SCJP(最近被甲骨文更名为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 m
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
的解释方式是:
子对象test
对象,并且具有有效的覆盖
对于getObject()
,请使用重写的方法这是不是JVM忽略了
getObject()
部分,总是从c1
中获取x
作为编译时关联的变量?尽管对suboviantTest正确地完成了覆盖,但由于变量c1的声明方式,答案是5。它被声明为协变项,而不是次协变项
当运行c1.getObject().x时,它不知道这是一个测试(没有使用强制转换)。这就是为什么协变项返回5,而不是次协变项测试返回6
如果你改变
System.out.println(c1.getObject().x);
到
你会得到你期望的6分
编辑:正如评论中指出的那样
“在Java中,字段不是多态的。只有方法是多态的。子类中的x隐藏基类中的x。它不会覆盖它。”(感谢JB Nizet)您正在从
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不会覆盖属性,因此它从A
获取x
,这里发生的技术术语是“隐藏”。Java中的变量名称由引用类型解析,而不是由它们引用的对象解析
- 对象具有A.x变量李>
- B对象同时具有A.x和B.x变量
但是,具有相同签名的实例方法是“重写的”而不是“隐藏的”,并且您无法访问从外部重写的方法的版本
请注意,隐藏也适用于具有相同签名的静态方法
简化形式的模拟问题(无覆盖):
好吧,我知道现在回答这个问题有点晚了,但我和我的朋友也有同样的问题,这里已经给出的答案对我们来说还不太清楚。因此,我只想说明我遇到了什么问题,以及它现在的意义:)
现在我明白了字段不会被重写,而是像miller.bartek指出的那样被隐藏,我也明白了重写是针对方法的,而不是Scott指出的字段
然而,我遇到的问题是这个。据我说,
c1.getObject().x
这必须转化为:
new B().x // and not newA().x since getObject() gets overrided
结果是6
我不明白为什么类A(超级类)的变量会被类B(子类)的对象调用,而没有明确要求这样的行为
从问题的措辞猜,我觉得OP也有同样的问题/疑问
我的答覆是:
你从埃尔贝克的回答中得到了一个暗示。在main方法中放入以下行并尝试编译代码:
A a = c1.getObject(); //line 1
B b = c1.getObject(); //line 2
您会注意到第1行是完全合法的,而第2行给出了编译错误
因此,当调用函数getObject()时,协变(super)函数将被subcoveriantTest(sub)函数覆盖,因为这在代码中是有效的覆盖,c1.getObject()将返回新的B()
但是,由于超级函数返回类类型a的引用,即使在被重写之后,它也必须返回类类型a的引用,当然,除非我们对其进行类型转换。这里,类B是类a(由于继承)
实际上,我们从c1.getObject()得到的不是
但这是:
(A) new B()
这就是为什么即使返回了类B的对象,并且类B的值x为6,输出结果仍然是5。重写方法时,会调用子类方法,当变量被重写时,当子类和父类都有一个同名变量时,将使用超类变量。子类的变量隐藏父类的变量,这称为变量隐藏
虽然变量隐藏看起来像是重写一个变量,类似于方法重写,但事实并非如此,重写只适用于方法,而隐藏是适用于变量的
在方法重写的情况下,重写的方法完全替换继承的方法,因此当我们试图通过持有子对象从父引用访问方法时,将调用子类中的方法
但是在变量隐藏中,子类隐藏继承的变量,而不是替换,所以当我们试图通过持有子对象从父引用访问变量时,它将从父类访问
如果子类中的实例变量与超类中的实例变量同名,则从引用类型中选择实例变量
你可以阅读更多关于我的文章你确定吗?我很确定多态性的全部要点是能够做到这样:Paint[]Paint={new RedPaint(),new BluePaint(),new GreenPaint()};'然后为(Paint p:Paint)Paint.getColor();`其中,Paint
的每个子类都有一个对getColor()
的有效重写。无论是否知道子类或是否需要对其进行强制转换,每个字段都应该执行不同的操作。只有方法是有效的。子类中的x隐藏了th中的x
A a = c1.getObject(); //line 1
B b = c1.getObject(); //line 2
new B()
(A) new B()