为什么Java在编译时绑定变量?

时间:2015-09-06 11:14:10

标签: java inheritance jls

考虑以下示例代码

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的重新定义会影响基类中的那个 为什么变量的绑定发生在编译时而不是在运行时?这只是出于性能原因吗?

4 个答案:

答案 0 :(得分:45)

原因在Section 15.11中的一个示例中的Java语言规范中进行了解释,引用如下:

  

...

     

最后一行表明,实际上,访问的字段不依赖于引用对象的运行时类;即使s包含对类T的对象的引用,表达式s.x也会引用类x的{​​{1}}字段,因为表达式Ss。 T类的对象包含两个名为S的字段,一个用于类x,另一个用于其超类T

     

缺少对字段访问的动态查找,可以通过简单的实现有效地运行程序。可以使用后期绑定和覆盖的功能,但仅在使用实例方法时 ...

所以是的,表现是一个原因。如何评估字段访问表达式的规范如下:

  
      
  • 如果该字段不是S

         

    ...

         
        
    • 如果该字段是非空static,则结果是在 Primary <值引用的对象中找到的final类型中的指定成员字段的值/ em>的
    •   
  •   

其中主要在您的案例中引用T类型的变量derived

正如@Clashsoft所建议的那样,另一个原因是,在子类中,字段未被覆盖,它们是隐藏。因此,允许根据声明的类型或使用强制转换来访问哪些字段是有意义的。对于静态方法也是如此。这就是根据声明的类型确定字段的原因。与实例方法的覆盖不同,它取决于实际类型。上面的JLS引用确实隐含地提到了这个原因:

  

可以使用后期绑定和覆盖的强大功能,但仅限于使用实例方法时。

答案 1 :(得分:25)

虽然您可能对性能有所了解,但还有另一个原因导致无法动态调度字段:如果您有MyClass.var实例,则根本无法访问MyDerivedClass字段。

通常,我不知道任何实际具有动态变量分辨率的静态类型语言。但是如果你确实需要它,你可以制作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;
    }
}

答案 2 :(得分:4)

java语言的多态行为适用于方法而不是成员变量:它们设计了在编译时绑定成员变量的语言。

答案 3 :(得分:1)

在java中,这是设计的。 因为,要动态解析的字段设置会使运行速度变慢。实际上,没有任何理由这样做。 因为,您可以在任何类私有中创建字段,并使用方法访问它们动态解析

因此,字段可以在编译时更好地解决:)