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());
}
}
我的理论是实例变量是静态查找的,与方法不同,但我不确定为什么会这样。
谢谢!
答案 0 :(得分:4)
在B
中,来自x
的字段A
被遮蔽(隐藏)而不是覆盖。要回答“为什么会这样”对文档here和here的引用。编译器将根据包含对象的类型从x
的两个实例中选择一个。 b0
的类型为A
A b0 = new B();
另一方面,当您定义(getter)方法时,这些方法可以覆盖父类中具有相同签名的方法。另一个令人讨厌的惊喜是父类中的字段被遮蔽,即使它是不同的类型。
成员的阴影被认为是一种不好的做法,因为它往往会使开发人员感到困惑。
答案 1 :(得分:4)
由于您要访问班级x
中的变量A
,因为b0
定义为A
。它被称为隐藏变量,而不是因为您可能怀疑覆盖变量,这在java中是不可能的。如果您使用typeCast从x
访问b0
,则会获得预期结果。
A b0 = new B();
System.out.print(((B)b0).x);
通过使用typeCast,您现在将从类x
访问变量B
。
有关详细信息,请阅读JLS 8.3
答案 2 :(得分:3)
静态字段不是继承的,它们不会互相覆盖,它们会相互影响。在您的情况下直接访问同名的静态字段时,超类的字段隐藏了另一个字段,并且您有两个int Xes但是超类中的一个不被隐藏并被选中。更好的是,当你调用相同方法的另一个实例并访问相同的静态字段时,就会发现事情变得非常奇怪。静态字段加在一起,最终X可能是5 + 5 = 10.
另一方面,如果从超类继承非静态字段,则不存在子类中具有不同值的问题,因为子类可以覆盖非静态超级成员。
静态变量和继承是不好的,它打破了你最不希望它的多态性。 (实际上,如果你理解你的语言的概念,你期望它,但其他人可能不会)