Why is an instance variable of the superclass not overridden by a subclass?

See my code below, in which the method print is overridden but variable a is not. Why is it allowed to declare duplicate variables in a subclass?

class B {
    int a = 10;
    public void print() {
        System.out.println("inside B superclass");
    }
}

class C extends B {
    int a = 20;
    public void print() {
        System.out.println("inside C subclass");
    }
}

public class A {
    public static void main(String[] args) {
        B b = new C();
        b.print(); // prints: inside C subclass
        System.out.println(b.a); // prints superclass variable value 10
    }
}

Why instance variable of a superclass is not overridden in subclass method see my code below ...

Because instance variables CANNOT be overridden in Java. In Java, only methods can be overridden.

When you declare a field with the same name as an existing field in a superclass, the new field hides the existing field. The existing field from the superclass is still present in the subclass, and can even be used ... subject to the normal Java access rules.

(In your example, an instance of C has two distinct fields called a, containing distinct values.)


Because instance variables CANNOT be overridden in Java, but why? why is it done in this manner in Java? What's the reason?

Why did they design it that way?

  1. Because overriding variables would fundamentally break code in the superclass. For example, if an override changes the variable's type, that is likely to change the behavior of methods declared in the parent class that used the original variable. At worst, it renders them uncompilable.

    For example:

       public class Sup {
           private int foo;
           public int getFoo() {
               return foo;
           }
       }
    
       public class Sub extends Sup {
           private int[] foo;
           ...
       }
    

    If Sub.foo overrides (i.e. replaces) Sup.foo, how can getFoo() work? In the subclass context, it would be trying to return a value of a field of the wrong type!

  2. If fields that were overridden were not private, it would be even worse. That would break the Liskov Substitutability Principle (LSP) in a pretty fundamental way. That removes the basis for polymorphism.

  3. On the flipside, overriding fields would not achieve anything that cannot be done better in other ways. For example, a good design declares all instance variables as private and provides getters/setters for them as required. The getters/setters can be overridden, and the parent class can "protect" itself against undesirable overrides by using the private fields directly, or declaring the getters/setters final.


References:

  • Java Tutorial - Hiding Fields
  • JLS Example 8.3.1.1-3 - Hiding of Instance Fields.

You may refer following section / examples in Java language specification that explains about the topic.

  1. Example 8.3.1.1-3. Hiding of Instance Variables
  2. Section 8.4.8. Inheritance, Overriding, and Hiding and related examples

Rest of my post is an additional information for those who are interested in scratching the surface of jvm internals on this subject. We can start by examining the byte codes generated for class A using javap. Following disassembles the byte codes into a human readable text based instructions (mnemonics).

javap -c A.class 

Without getting lost in many details of the entire dis-assembly, we can focus on the lines corresponding to b.print and b.a

9: invokevirtual #4                  // Method B.print:()V
...
...
16: getfield      #6                  // Field B.a:I

We can immediately infer that the op codes used for accessing the method and a variable are different. If you are from a C++ school, you could sense that all method calls are virtual by default in java.

Now let us write another class A1 identical to A, but just has a casting for accessing variable 'a' in C.

public class A1 {
  public static void main(String[] args) {
    B b=new C();
    b.print(); //casting is irrelevant here because methods are anyway bound at runtime     System.out.println(((C)b).a);// the casting allows us to access of value of a in C
  }
}

Compile the file and disassemble the class.

javap -c A1.class

You would notice that dis-assembly now points to C.a instead of B.a

19: getfield #6 // Field C.a:I

if you want to dig deep into this, here goes additional information:
- invokevirtual corresponds to opcode 0xb6
- getfield corresponds to opcode 0xb4

You can find a JVM specification that explains comprehensively about these opcodes at - http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html
Check out in amazon.com for "Java Virtual Machine" books that could make life little more easier for decoding the specification.