Why does Java bind variables at compile time?

JavaInheritanceJls

Java Problem Overview


Consider the following example code

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();
	}
}

it gives the following output

base
base
base
derived

Method calls are resolved at runtime and the correct overridden method is called, as expected.
The variables access is instead resolved at compile time as I later learned. I was expecting an output as

base
derived
base
derived

because in the derived class the re-definition of var shadows the one in the base class.
Why does the binding of variables happens at compile time and not at runtime? Is this only for performance reasons?

Java Solutions


Solution 1 - Java

The reason is explained in the Java Language Specification in an example in Section 15.11, quoted below:

>... > >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 class T, the expression s.x refers to the x field of class S, because the type of the expression s is S. Objects of class T contain two fields named x, one for class T and one for its superclass S. > >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...

So yes performance is a reason. The specification of how the field access expression is evaluated is stated as follows:

> - 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 type T found in the object referenced by the value of the Primary.

where Primary in your case refers the variable derived which is of type MyClass.

Another reason, as @Clashsoft suggested, is that in subclasses, fields are not overriden, they are hidden. So it makes sense to allow which fields to access based on the declared type or using a cast. This is also true for static methods. This is why the field is determined based on the declared type. Unlike overriding by instance methods where it depends on the actual type. The JLS quote above indeed mentions this reason implicitly:

>The power of late binding and overriding is available, but only when instance methods are used.

Solution 2 - Java

While you might be right about performance, there is another reason why fields are not dynamically dispatched: You wouldn't be able to access the MyClass.var field at all if you had a MyDerivedClass instance.

Generally, I don't know about any statically typed language that actually has dynamic variable resolution. But if you really need it, you can make getters or accessor methods (which should be done in most cases to avoid public fields, anyway):

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;
    }
}

Solution 3 - Java

The polymorphic behaviour of the java language works with methods and not member variables: they designed the language to bind member variables at compile time.

Solution 4 - Java

In java, this is by design. Because, the set up of fields to be dynamically resolved would make things to run a bit slower. And in real, there's not any reason of doing so. Since, you can make your fields in any class private and access them with methods which are dynamically resolved.

So, fields are made to resolved better at compile time instead :)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlessandro Da RugnaView Question on Stackoverflow
Solution 1 - JavaM AView Answer on Stackoverflow
Solution 2 - JavaClashsoftView Answer on Stackoverflow
Solution 3 - JavaalainlompoView Answer on Stackoverflow
Solution 4 - JavaNIKHIL CHAURASIAView Answer on Stackoverflow