我准备了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 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
是:
- 打电话的getObject上C1
- C1实际上是一个
SubCovariantTest
对象,并具有用于有效超越getObject()
所以使用重写方法 - 倍率返回B,所以抓从x B,其是6
难道是JVM忽略的情况下getObject()
的一部分,并且总是把x
从c1
作为变量是在编译时关联?
虽然倍率为SubCovariantTest得当的答案是5因变量C1的声明方式。 它被声明为CovariantTest而不是作为SubCovariantTest。
当c1.getObject()。x被运行时,它不知道这是一个SubCovariantTest(没有使用铸造)。 这就是为什么5是从CovariantTest从SubCovariantTest返回,而不是6。
如果你改变
System.out.println(c1.getObject().x);
至
System.out.println(((SubCovariantTest) c1).getObject().x);
你会得到6如你预期。
编辑:正如在评论中指出
“字段不是在Java中的多态性,只有方法,在子类中的X隐藏了基类中的X,它不会覆盖它。” (感谢JB Nizet)
对这里发生的一切的技术术语为“隐藏”。 在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
}
}
好吧,我知道这是一个有点晚答复这个问题,但我和我的朋友有同样的问题,并已经在这里的答案也不太清楚它给我们。 所以我只是说出我有什么问题,现在它是如何有意义:)
现在我明白,表格中没有得到overrided而是他们得到隐藏的miller.bartek指出,我也明白,压倒一切的是方法和斯科特指出未领域。
但是我有这个问题是这样的。 据我,
c1.getObject().x
这必须转化为:
new B().x // and not newA().x since getObject() gets overrided
而计算结果为6。
而且我不知道为什么A类(超类)的变量是由B类(子类)的对象,而不必明确要求对这种行为被调用。
而从问题的措辞猜测,我觉得OP心中有同样的问题/疑问。
我的答案:
你从Elbek的答案的提示。 把下面的行中的主要方法,并尝试编译的代码:
A a = c1.getObject(); //line 1
B b = c1.getObject(); //line 2
你会发现,1号线是完全合法的,而第2行给出编译错误。
该功能的getObject()被调用所以,当该CovariantTest(超级)功能越来越被SubCovariantTest(子)功能overrided因为这是在代码和c1.getObject(有效压倒一切)将返回新B()。
然而,由于超函数返回类型A的参考,甚至后得到overrided,它必须返回类型A的引用,除非ofcourse我们类型强制转换。 在这里,B类是A类(因继承)。
所以实际上,我们正在从c1.getObject获得()不
new B()
但是这个:
(A) new B()
这就是为什么输出出来是即使被返回类B的物体5和B类具有x值作为6。
您致电方法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
当方法被覆盖,子类方法被调用,当变量被覆盖使用超变量
当孩子与父类都具有相同名称的子类的变量变量隐藏父类的变量,这就是所谓的变量隐藏。
虽然可变隐藏看起来像覆盖变量类似于方法压倒一切,但它不是,重写仅适用于方法,同时隐藏适用变量。
在方法重载的情况下,覆盖的方法完全取代继承的方法,所以,当我们尝试抱着孩子的对象访问来自父母的参考方法,从子类中的方法被调用。
但是,在可变隐藏子类隐藏继承的变量,而不是替换,所以当我们尝试抱着孩子的对象访问来自父母的参考变量,它会从父类访问。
当在子类中的实例变量具有相同的名称作为一个超类的一个实例变量,则实例变量从参考类型来选择。
你可以阅读更多关于我的文章什么是可变的阴影和隐藏在Java中。