静态实例变量查找 Java

Static Instance Variable Lookup Java

class A {int x = 5;}
class B extends A {int x = 10;}
class D {
    public static void main(String[] args){
        A b0 = new B();
        System.out.print(b0.x);
    }
}

我想知道为什么这段代码打印 5 而不是 10。

如果我改为编写以下内容,将变量 x 转换为方法,它会像我预期的那样工作,并打印出 10,因为在编译时它只是检查 b0 的静态类型 A 是否有方法x,然后在 运行 时,使用 b0 的动态类型 B 到 运行 x。

class A {int x() {return 5;}}
class B extends A {int x() {return 10;}}
class D {
    public static void main(String[] args){
        A b0 = new B();
        System.out.print(b0.x());
    }
}

我的理论是,与方法不同,实例变量是静态查找的,但我不确定为什么会这样。

谢谢!

B 中,来自 A 的字段 x 被隐藏(隐藏)而不是被覆盖。回答 "why that would be" 对文档 here and here 的引用。编译器将根据包含对象的类型选择 x 的 2 个实例之一。 b0 的类型是 A

 A b0 = new B();

另一方面,当您定义 (getter) 方法时,这些方法可以覆盖父 class 中具有相同签名的方法。另一个令人讨厌的惊喜是父 class 中的一个字段被隐藏,即使它是不同的类型。

隐藏成员被认为是一种不好的做法,因为它往往会使开发人员感到困惑。

因为您正在从 class A 访问变量 x,因为 b0 被定义为 A。它被称为隐藏变量,而不是你可能怀疑的覆盖变量,这在 java 中是不可能的。如果您使用 typeCast 从 b0 访问 x,您将获得预期的结果。

A b0 = new B();
System.out.print(((B)b0).x);

通过使用 typeCast,您现在可以从 class B 访问变量 x

有关更多信息,您可以通读 JLS 8.3

静态字段不被继承,它们不相互覆盖,它们相互遮蔽。当您在您的案例中直接访问同名的静态字段时, superclass 的字段隐藏了另一个字段并且您有两个 int Xes 但 superclass 中的一个未隐藏并被选中.更好的是,当您调用同一方法的另一个实例并访问同一静态字段时,事情就会变得非常奇怪。将静态字段加在一起,您最终可能会得到 X 5+5 = 10。

另一方面,如果你从 superclass 继承非静态字段,它在 sub-class 中具有不同的值是没有问题的,因为 sub-class ] 可以覆盖非静态超级成员。

静态变量和继承是不好的,它破坏了你最意想不到的多态性。 (实际上,如果你理解你的语言的概念,你会期望它,但其他人可能不会)