Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

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
    }
}
like image 622
Jayesh Avatar asked Aug 23 '12 06:08

Jayesh


People also ask

Why variables are not overridden in Java?

Because variables in Java do not follow polymorphism and overriding is only applicable to methods but not to variables. And when an instance variable in a child class has the same name as an instance variable in a parent class, then the instance variable is chosen from the reference type.

Is a subclass an instance of a superclass?

A class in Java can be declared as a subclass of another class using the extends keyword. A subclass inherits variables and methods from its superclass and can use them as if they were declared within the subclass itself: class Animal { float weight ; ...

Do subclasses inherit instance variables?

Subclasses do not have access to the private instance variables in a superclass that they extend. Constructors are not inherited.

Can instance variables be inherited in Java?

I know that instance variable are not inherited but they can be accessed in sub class. If they can be accessed in sub class then does that means that they are shared between super class and subclass or both super class and subclass have different copy.


Video Answer


3 Answers

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.
like image 196
Stephen C Avatar answered Oct 10 '22 05:10

Stephen C


I have modified your code for easy Explanation ,instead of variable 'a' ,lets say the Class C contains variable 'c'. Its for the same reason why class C cannot access the instance variable of Class c itself without Typecasting. Example Given below

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

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


 }
public class A  {
    public static void main(String[] args) {
        B b=new C();

        System.out.println(b.x);//will throw compile error unless b is type casted to Class C

    }

}

So,in java,the compiler goes by the reference,not by the instance. To get over this compiler uses Runtime polymorphism,but it is for the methods,not the instance variables. So variables cannot be accessed without type-casting and the methods unless,overridden(Runtime polymoprhism),cannot be accessed without type-casting.

So,in our case.it is obvious for refrence of Superclass carrying an instance of subclass,to view in the superclass.

like image 3
Sriharsha g.r.v Avatar answered Oct 10 '22 07:10

Sriharsha g.r.v


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.

like image 4
Shree Avatar answered Oct 10 '22 05:10

Shree