考虑以下示例代码
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
方法调用在运行时得到解决,并按预期调用正确的覆盖方法。
正如我后来了解到的,变量访问是在编译时解决的。
我期待输出为
base
derived
base
derived
因为在派生类中重新定义 var
会影响基类中的定义。
为什么变量的绑定(bind)发生在编译时而不是运行时?这仅仅是出于性能原因吗?
最佳答案
原因在 Java 语言规范中的 Section 15.11 示例中进行了解释。 ,引述如下:
...
The last line shows that, indeed, the field that is accessed does not depend on the run-time class of the referenced object; even if
s
holds a reference to an object of classT
, the expressions.x
refers to thex
field of classS
, because the type of the expressions
isS
. Objects of class T contain two fields namedx
, one for classT
and one for its superclassS
.This lack of dynamic lookup for field accesses allows programs to be run efficiently with straightforward implementations. The power of late binding and overriding is available, but only when instance methods are used...
所以是的,性能是一个原因。字段访问表达式的求值方式规范如下:
If the field is not
static
:...
- If the field is a non-blank
final
, then the result is the value of the named member field in typeT
found in the object referenced by the value of the Primary.
在您的情况下,Primary 是指 derived
类型的变量 MyClass
。
另一个原因,正如@Clashsoft 建议的那样,是在子类中,字段没有被覆盖,它们是隐藏。因此,允许基于声明的类型或使用强制类型转换访问哪些字段是有意义的。这也适用于静态方法。这就是为什么根据声明的类型确定字段的原因。与依赖于实际类型的实例方法覆盖不同。上面的 JLS 引用确实含蓄地提到了这个原因:
The power of late binding and overriding is available, but only when instance methods are used.
关于java - 为什么Java在编译时绑定(bind)变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32422923/