关于覆盖变量的地方的轻微混淆

时间:2012-09-25 18:50:04

标签: java override scjp ocpjp

我正在为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的方式是:

  1. 在c1上调用getObject
  2. c1实际上是一个SubCovariantTest对象,并且具有有效覆盖 对于getObject(),请使用重写方法
  3. 覆盖返回B,因此从B抓取x,即6
  4. 是JVM忽略getObject()部分的情况,并且始终从x获取c1,因为变量在编译时关联了吗?

6 个答案:

答案 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中的变量名称由引用类型解析,而不是它们引用的对象。

  • 对象具有A.x变量。
  • B对象同时具有A.x和B.x变量。

但是,具有相同签名的实例方法被“覆盖”而不是“隐藏”,并且您无法访问从外部覆盖的方法版本。

请注意,隐藏也适用于具有相同签名的静态方法。

您的模拟问题以简化形式(没有覆盖):

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)

您正在调用c1System.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>

但是在变量隐藏中,子类隐藏了继承的变量而不是替换,因此当我们尝试通过保存子对象来从父对象的引用中访问变量时,它将从父类访问。

当子类中的实例变量与超类中的实例变量同名时,则从引用类型中选择实例变量。

您可以在我的文章What is Variable Shadowing and Hiding in Java.

上阅读更多内容
相关问题