Search code examples
javainheritancepolymorphismsubclasssuperclass

Why does it store or allocate memory for super class variables, in sub class object?


In the following code-

class Mammal {
  String name = "furry ";
  String makeNoise() { 
    return "generic noise";
 }
}

class Zebra extends Mammal {
    String name = "stripes ";
    String makeNoise() { 
        return "bray"; 
    }
}

public class ZooKeeper {
    public static void main(String[] args) { 
       new ZooKeeper().go();
   }

   void go() {
      Mammal m = new Zebra();
      System.out.println(m.name + m.makeNoise());

      Zebra z = new Zebra();
      System.out.println(z.name + z.makeNoise());
   }
}

Both objects (m and z), if I see in debug windows of eclipse, contain both values of name variable (furry and stripes).

I do understand that in polymorphism, generic method of super class can be used by sub class as well. But why does sub class object stores values of super class variables as well, even in case of hiding. Is there any use of this?


Solution

  • First: As a general rule, if a class defines a field that a subclass can access, the subclass should not redefine the field. It's just a Really Bad Idea. Primarily what you're seeing is there to make private fields work properly. Redefining non-private fields in a subclass is asking for a world of hurt. (Of course, if Joe writes Mammal and Mary writes Zebra and at some point Joe adds a field to Mammal that happens to conflict with one that Mary used in Zebra, there's nothing Mary can do about that. Which is one reason for making all fields private.)

    But why does sub class object stores values of super class variables as well, even in case of hiding.

    The key here is to remember that fields are not polymorphic, just methods. So there have to be two name fields in the object (one from Mammal and one from Zebra), because code using a Mammal-typed reference needs to see the Mammal name, whereas code using the Zebra-typed reference needs to see the Zebra name.

    That's why your code shows "furry bray" and then "stripes bray". You get "furry bray" via m because accessing name on m (a Mammal-typed variable) accesses Mammal's name (not polymorphic), giving you "furry". But then you call the method makeNoise using m and get back "bray", because the method that gets called is the one on Zebra (polymorphic). Then you do it again with z (a Zebra-typed reference) and see "stripes bray" because z accesses Zebra's name, not Mammal's.


    The next question you might have is: If we change makeNoise in both classes to:

    String makeNoise() { 
        return this.name;
    }
    

    why does ZooKeeper's code

      Mammal m = new Zebra();
      System.out.println(m.name + m.makeNoise());
    
      Zebra z = new Zebra();
      System.out.println(z.name + z.makeNoise());
    

    give us "furry stripes" from m and stripes stripes from z?

    It's the same reason, just a different presentation of it. m.name accesses name from a Mammal-typed reference, and so sees Mammal's name (not polymorphic). m.makeNoise calls Zebra's makeNoise method (polymorphic), and inside Zebra's makeNoise, this has the type Zebra even though we called it from a Mammal-typed m (and so this.name uses Zebra's name). The fact that Zebra's makeNoise is used there, and the fact that this within Zebra code is typed Zebra are both key to polymorphism in Java.

    Let's take it further: What if Zebra doesn't define makeNoise at all?

    class Mammal {
        String name = "furry ";
        String makeNoise() { 
            return this.name;
        }
    }
    
    class Zebra extends Mammal {
        String name = "stripes ";
    }
    

    Now we get "furry furry" from m and "stripes furry" from z. And the reason for it is the same as above: The type of the reference determines which field is used, and in Mammal code (makeNoise), this has the type Mammal. So even though we called makeNoise using z, since Zebra has no makeNoise, Mammal's is called, so the reference that looks up name has the type Mammal.

    Is there any use of this?

    It's crucial to classes working properly, particularly in the case of private fields. Mammal code doesn't have to worry about a subclass coming along and redefining its fields. You could have a 10-deep class hierarchy, with each class defining its own name, and that's fine, each level's code works with the name it defines.