为什么Java在编译时绑定variables?
考虑下面的示例代码
class MyClass { public String var = "base"; public void printVar() { System.out.println(var); } } class MyDerivedClass extends MyClass { public String var = "derived"; public void printVar() { System.out.println(var); } } public class Binding { public static void main(String[] args) { MyClass base = new MyClass(); MyClass derived = new MyDerivedClass(); System.out.println(base.var); System.out.println(derived.var); base.printVar(); derived.printVar(); } }
它给出以下输出
base base base derived
方法调用在运行时被parsing,正如预期的那样调用正确的重写方法。
我后来得知,访问variables是在编译时parsing的。 我期待输出为
base derived base derived
因为在派生类中, var
的重定义会影响基类中的一个。
为什么variables绑定发生在编译时,而不是在运行时? 这只是出于性能的原因?
Java语言规范中的原因在第15.11节的一个例子中进行了解释,引用如下:
…
最后一行显示,确实被访问的字段不依赖于引用对象的运行时类; 即使
s
持有对T
类对象的引用,expression式sx
引用类S
的x
字段,因为expression式s
的types是S
T类的对象包含两个名为x
字段,一个用于T
类,另一个用于其超类S
这种缺乏对字段访问的dynamic查询允许程序以直接的实现方式高效运行。 后期绑定和覆盖的力量是可用的,但只有当使用实例方法时 …
所以是的performance是一个原因。 如何评估字段访问expression式的规范如下:
如果该字段不是
static
:…
- 如果该字段是非空白的
final
,那么结果是在由主值的引用的对象中find的typesT
中的命名成员字段的值。
在你的情况下, 主要是MyClass
types为MyClass
的variables。
另一个原因,就像@Clashsoft所说,在子类中,字段不会被覆盖,它们是隐藏的 。 因此,根据声明的types或使用强制types来允许哪些字段被访问是有意义的。 静态方法也是如此。 这就是为什么该字段是基于声明的types确定的。 与重写实例方法不同,它取决于实际的types。 上面的JLS报价确实提到了这个原因:
后期绑定和覆盖的function是可用的,但仅在使用实例方法时才有效。
虽然你可能对性能是正确的,但还有一个原因是为什么字段没有被dynamic调度:如果你有一个MyDerivedClass
实例,你将无法访问MyClass.var
字段。
一般来说,我不知道任何实际上具有dynamic可变分辨率的静态types语言。 但是,如果你确实需要它,你可以使用getter或者accessor方法(在大多数情况下应该避免使用public
字段):
class MyClass { private String var = "base"; public String getVar() // or simply 'var()' { return this.var; } } class MyDerivedClass extends MyClass { private String var = "derived"; @Override public String getVar() { return this.var; } }
java语言的多态行为适用于方法而不是成员variables:他们在编译时devise了绑定成员variables的语言。
在java中,这是devise。 因为,要dynamic解决的字段的设置会使事情运行得慢一点。 而实际上,没有任何理由这样做。 因为,你可以使任何类中的字段都是私有的 ,并用dynamicparsing的 方法访问它们。
所以,在编译时 ,字段被parsing得更好:)