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

Jim*_*mmy 11 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 main(String[]args) 
    {
       CovariantTest c1 = new SubCovariantTest();
       System.out.println(c1.getObject().x);
    }
}

class SubCovariantTest extends CovariantTest 
{
    public B getObject() 
    {
       return new B();
    }
}
Run Code Online (Sandbox Code Playgroud)

答案是5,但我选择了6.

我知道覆盖适用于运行时的方法,而不是变量,但我的思维解释方式println是:

  1. 在c1上调用getObject
  2. c1实际上是一个SubCovariantTest对象,并且具有有效覆盖getObject(),因此请使用重写方法
  3. 覆盖返回B,因此从B抓取x,即6

是JVM忽略该getObject()部分的情况,并且总是xc1编译时关联变量?

Sco*_*ott 12

尽管对SubCovariantTest正确完成了覆盖,但由于声明了变量c1,因此答案为5.它被声明为CovariantTest,而不是SubCovariantTest.

运行c1.getObject().x时,它不知道它是SubCovariantTest(没有使用转换).这就是为什么5从CovariantTest返回而不是从SubCovariantTest返回6.

如果你改变了

System.out.println(c1.getObject().x);
Run Code Online (Sandbox Code Playgroud)

System.out.println(((SubCovariantTest) c1).getObject().x);
Run Code Online (Sandbox Code Playgroud)

你会得到6你所期望的.

编辑:正如评论中指出的那样

"字段不是在Java中的多态性,只有方法,在子类中的X隐藏了基类中的X,它不会覆盖它." (感谢JB Nizet)

  • 是的,但字段在Java中不是多态的.只有方法.子类中的x隐藏了基类中的x.它不会覆盖它.除了封装之外,这是不使用公共字段的另一个好理由. (9认同)

mas*_*817 6

好的,我知道现在回答这个问题有点晚了,但我和我的朋友遇到了同样的问题,这里的答案对我们来说还不是很清楚。所以我只说明我遇到了什么问题以及现在它是如何有意义的:)

现在我明白字段不会被覆盖,而是像miller.bartek指出的那样被隐藏,我也明白覆盖是针对方法的,而不是斯科特指出的字段。

然而,我遇到的问题是这个。在我看来,

c1.getObject().x
Run Code Online (Sandbox Code Playgroud)

这必须转化为:

new B().x     // and not newA().x since getObject() gets overrided
Run Code Online (Sandbox Code Playgroud)

计算结果为 6。

而且我不明白为什么 A 类(超类)的变量在没有明确要求这种行为的情况下被 B 类(子类)的对象调用。

从问题的措辞猜测,我觉得 OP 有同样的问题/怀疑。


我的答案:

您可以从 Elbek 的回答中得到提示。将以下几行放在 main 方法中并尝试编译代码:

A a = c1.getObject();    //line 1
B b = c1.getObject();    //line 2
Run Code Online (Sandbox Code Playgroud)

您会注意到第 1 行是完全合法的,而第 2 行给出了编译错误。

因此,当调用函数 getObject() 时,CovariantTest(超级)函数将被 SubCovariantTest(子)函数覆盖,因为这是代码中的有效覆盖,并且 c1.getObject() 将返回新的 B()。

然而,由于超级函数返回类类型 A 的引用,即使在被覆盖之后,它也必须返回类类型 A 的引用,除非我们对它进行类型转换。在这里,B 类A 类(由于继承)。

所以实际上,我们从 c1.getObject() 得到的不是

new B()
Run Code Online (Sandbox Code Playgroud)

但是这个:

(A) new B()
Run Code Online (Sandbox Code Playgroud)

这就是为什么即使返回了类 B 的对象并且类 B 的 x 值为 6,输出仍为 5。

  • 是的,因为对于 Java 5.0 及更高版本,协变覆盖成为一种语言特性。此新功能允许由具有相同签名但返回类型不同的派生类方法覆盖方法,只要派生方法的 *return* 类型是从被覆盖方法的返回类型派生的。所以,如上所述,当一个类型为 CovariantTest 的变量被分配一个 SubCovariantTest 的实例时,getObject() 返回的值确实是 B 的一个实例,但它会被强制转换为类 A 以匹配 getObject 的返回类型() 在 CovariantTest 中。 (2认同)