Inheritance ConceptsHomepage  « Learn Java6 « Inheritance Concepts

In this lesson we continue our investigation of inheritance and conceptualize where our objects fit into a class hierarchy. In all our uses of inheritance so far we have looked at overriding a superclass method directly above the subclass we are in and all the classes used so far are a level below the top abstraction. This isn't the end of the story though, theoretically there is no limit to the amount of subclassing you can do, although common sense and sanity preclude this. It's uncommon to need subclassing of more than two or three levels although some of Javas GUI classes have more than this because of the nature of those classes. For our purposes we will add another level to our existing Vehicle tree to help illustrate the points of this lesson:

java inheritance 5 java inheritance 7 java inheritance 8

When we last visited Vehicle Town, we had subclassed the Vehicle class and things were looking good. But the mayor has been in touch we have a problem with the trucks; we need specialist trucks to haul provisions and clear up all the mess in Vehicle Town. Let's use inheritance again to subclass the Truck class and create these specialized trucks.

Here is the new specialized HGV class we need to code up and override the load() method from the Vehicle class.

Here is the new specialized Garbage class we need to code up and override the load() method from the Vehicle class.

Press the button below to cycle through our subclass enhancements.


IS-A and HAS-A Relationshipsgo to top of page Top

Before we code up our new Truck subclasses we need to talk about the relationships in our inheritance trees. How do we know what to make a superclass or subclass and what goes into our instance variables for these classes. There is a very simple way to check, by using the IS-A and HAS-A test, to see what sort of relationships our classes and data members have. So how do we do this?

For the IS-A, anywhere where you would use the extends keyword just replace it with IS-A and see how the relationship looks.

For the HAS-A test, use the class followed by HAS-A and then the name of the possible instance variable and see how the relationship looks.

Try the following test to get to grips with IS-A and HAS-A relationships:

IS-A and HAS-A Test
Statement Question
Hammer extends Toolbox
  • valid
  • invalid
Hammer IS-A Toolbox - no.
Hammer HAS-A Toolbox - no.
Reversed
Toolbox IS-A Hammer - still no.
Toolbox HAS-A Hammer - yes.
Car extends Vehicle
  • valid
  • invalid
Car IS-A Vehicle - yes.
Car HAS-A Vehicle - no.
Sink extends Kitchen
  • valid
  • invalid
Sink IS-A Kitchen - no.
Sink HAS-A Kitchen - no.
Reversed
Kitchen IS-A Sink - still no.
Kitchen HAS-A Sink - yes.
Bird extends Canary
  • valid
  • invalid
Bird IS-A Canary - no.
Bird HAS-A Canary - no.
Reversed
Canary IS-A Bird - yes.
Canary HAS-A Bird - no.
Window extends House
  • valid
  • invalid
Window IS-A House - no.
Window HAS-A House - no.
Reversed
House IS-A Window - still no.
House HAS-A Window - yes.

Using IS-A and HAS-A are quite a simple way gerat for checking inheritance relationships and the good thing is we can test for subclass suitability all the way down the inheritance tree. Lets take part of the inheritance tree from the slideshow above and check our new level of subclasses to make sure they pass the IS-A tests for all their superclasses before we code them up.

java inheritance 9

Well our new HGV class passes the IS-A tests for being a Truck and Vehicle so our inheritance tree looks good. We can also do the same for the Garbage class and it passes as well. We can now code up our new Truck subclasses:

Our HGV subclassgo to top of page Top

Here we use the extends keyword to subclass the Truck class. The HGV class is fairly easy and we just need to override the load() method from the Vehicle class.


/*
  A HGV Class
*/ 
public class HGV extends Truck {
    /*
      Our load() override method
    */ 
    public void load(String payload) {
        System.out.println("We are transporting " + payload + " in our Heavy Goods Vehicle." );
    }
}

Save and compile our HGV subclass in directory   c:\_OOConcepts in the usual way.

Lets test the new HGV subclass to make sure it works:


/*
  Test Class for HGV
*/ 
public class TestHGV {

    public static void main (String[] args) {
        HGV hgv = new HGV();
        hgv.setChassis("6-axle chassis");
        hgv.setMotor("24 stroke");
        hgv.setWheels(10);
        System.out.println("Our HGV has a " + hgv.getChassis() + ", " + hgv.getMotor() 
                                            + " motor and has " + hgv.getWheels() + " wheels.");
        hgv.service(4); 
        hgv.carry(24); 
        hgv.load("bulding materials"); 
    }
}

Save, compile and run the file in directory   c:\_OOConcepts in the usual way.

run HGV class

The above screenshot shows the output of testing our HGV subclass. The class works as intended and uses all its inherited members of the Vehicle superclass, our carry() method override from the Truck class and our override of the load() method to output some messages. Time to code up the Garbage class:

Our Garbage subclassgo to top of page Top

Here we use the extends keyword to subclass the Truck class. The Garbage class is the same as the HGV class, we just need to override the load() method from the Vehicle class.


/*
  A Garbage Class
*/ 
public class Garbage extends Truck {
    /*
      Our load() override method
    */ 
    public void load(String payload) {
        System.out.println("Our garbage truck eats " + payload);
    }
}

Save and compile our Garbage subclass in directory   c:\_OOConcepts in the usual way.

Lets test the new Garbage subclass to make sure it works:


/*
  Test Class for Garbage
*/ 
public class TestGarbage {

    public static void main (String[] args) {
        Garbage garbage = new Garbage();
        garbage.setChassis("2-axle chassis");
        garbage.setMotor("8 stroke");
        garbage.setWheels(6);
        System.out.println("Our garbage truck has a " + garbage.getChassis() + ", " 
                                   + garbage.getMotor() + " motor and has " 
                                   + garbage.getWheels() + " wheels.");
        garbage.service(2); 
        garbage.carry(10); 
        garbage.load("rubbish"); 
    }
}

Save, compile and run the file in directory   c:\_OOConcepts in the usual way.

run Garbage class

The above screenshot shows the output of testing our Garbage subclass. The class works as intended and uses all its inherited members of the Vehicle superclass, our carry() method override from the Truck class and our override of the load() method to output some messages.

Inheritance Method Invocationgo to top of page Top

Now all the Truck subclasses are coded and tested for our inheritance tree, it's time to take a closer look at which methods are invoked on our objects. We will use our last tested Truck subclass, the Garbage class to highlight which methods are called:

inheritance 10

Hopefully you can see from the image that the most specific method for the instance, at the lowest level on the inheritance tree, is the one that is used. We don't need to worry about unmatched reference types as the compiler has alredy ensured that a method is callable for a specific reference type, regardless of where it is in the inheritance tree. Of course, if we created a new Bus object and called say the load method, then the load method from Vehicle would be used. A superclass knows nothing of the members in the subclasses below it. So at runtime the JVM will always pick the right method to use, as all the methods are either inherited from superclasses or overridden / defined in the subclass itself.

Using the super keywordgo to top of page Top

Ok, we have instance variables and instance methods in our superclasses that we want to access from our subclasses, but how can we access them. We can get to our superclass members using the super keyword. We will look at how to use the super keyword with to access instance variables and methods before looking at overrides.

Accessing Superclass Membersgo to top of page Top

In the last lesson we mentioned the fact that we do not override instance variables in a subclass with the same identifier in the superclass. When we do this it is known as hiding and is not recommended as it can lead to confusing code. But we will show it here along with accessing superclass methods to see how it's done:


/*
  Superclass to illustrate super. access
*/ 
public class A { 
    int i = 1;
    int j = 1;
    public void a() {
        System.out.println("Accessing a in class A"); 
    }
}
/*
  Subclass to illustrate super. access
*/ 
public class B extends A { 
    int i = 2;
    String j = "two";
    public void print() {
        System.out.println("i in superclass = " + super.i);
        System.out.println("i in subclass = " + i);
        System.out.println("j in superclass = " + super.j);
        System.out.println("j in subclass = " + j);
        super.a();
        b();
    }
    public void b() {
        System.out.println("Accessing b in class B"); 
    }
}
/*
  Test class to illustrate super. access
*/ 
public class TestAB {
    public static void main(String[] args) {
        B b = new B();
        b.print();
    }
}

In the above code the instance variables i and j in class B hide the same named in class A. We are actually redefining the j instance variable to a String object. We use the super keyword to access the superclass instance variables and methods and print everything off.

run super members

The above screenshot shows the output of running our TestAB test class after compiling classes A and B.

Accessing Superclass Overridesgo to top of page Top

Wouldn't it be nice if we only had to partly implement code in the overridden methods of our subclasses and left the more generic code in the superclass. This is the whole point of subclassing after all; to avoid duplication and enhance reusability. We can have partial implementations we can access and use, we do this using the super keyword:


/*
  Superclass to illustrate super. overrides
*/ 
public class A { 

    public void a() {
        System.out.println("All the generic code from the superclass"); 
    }
}
/*
  Subclass to illustrate super. overrides
*/ 
public class B extends A { 

    public void a() {
        super.a();
        System.out.println("All the specific code for the subclass"); 
    }
}
/*
  Test class to illustrate super. overrides
*/ 
public class TestAB {
    public static void main(String[] args) {
        B b = new B();
        b.a();
    }
}

It is very easy to use the superclass to implement generic code , we just need to put in a call to the superclass method from our subclass override method using super and it's done. The actual syntax used is exactly the same as for any other member but goes in the override method. Just remember to put it before any subclass code in the override so you don't overwrite any subclass specific stuff.

run super override

The above screenshot shows the output of running our TestAB test class after compiling classes A and B.

Superclass Constructorsgo to top of page Top

And what about constructors, what happens with them when we use inheritance?. We know the compiler creates a no-args constructor for us if we don't supply one, is this the same for inheritance? Before we answer these questions, we need to first look back to the first Java class we wrote on the site, the Hello.java class:


public class Hello {
   public static void main (String[] args) {
      System.out.println("Hello World");
   }
}

Unbeknown to us, this simple program was implicitly extended by the compiler, in fact every program we write has an implicit superclass called Object. How can this be though, how can our Garbage class extend Truck and Object, we know we can only extend one class. Well the compiler just travels up the inheritance tree and tries to extend our Truck class. The Truck class extends the Vehicle class so the compiler travels up the tree again. The Vehicle class extends nothing so the compiler implicitly extends the Vehicle class with Object. What this means for us is that we get to inherit the methods of Object which we will talk about in the Object Superclass lesson later in this section. For now, we just need to know that we inherit from Object at the top of our hierarchy.

Getting back to our original question about constructors and inheritance, if we haven't supplied a constructor, then the compiler inserts a no-args constructor in each of our subclasses for us, the same as normal. Not only that the compiler also adds super() as the first statement within our constructors. What does the call to super() do? It calls the constructor in the superclass and if this has a superclass, you can see where this is going, it also calls super(). Even abstract superclasses have constructors, we just can't see or use them. This continues all the way up the inheritance tree until we reach Object and is known as constructor chaining. But why does the compiler do this I hear you ask! Well if we think about it, our subclass might need state from any of its superclasses instance variables or need that state to use superclass methods and it gets them from calling super(). Even if the instance variables have private access our subclass still needs the methods that use them. The calls to super() just get added to the stack until we hit Object and then are removed from the stack as they get processed. It's time for a slideshow to explain constructor chaining, just press the button to step through it.

java stack 1 super java stack 2 super java stack 3 super java stack 4 super java stack 5 super

Press the buttons below to cycle through our stack explanation.


As you can see calling new on an object involves a lot of work for the JVM behind the scenes. The point to get from the slideshow is that even though the B class gets pushed onto the stack first the code after the implicit call to super() is actually the last to be executed. The Object code will always execute first and then down the constructor chain until we get to the actual objects code we instantiated. This makes sense, because each subclass might have dependencies on the object state of the superclass above it. But what about constructors with arguments, doesn't the compiler always insert a call to super() for us? Well yes it does, if we don't supply a call to super() ourselves. If fact we have complete control over how our objects are constructed because we have the option of supplying arguments when we code our own calls to super(). And if we do so, then the constructor in the superclass matching the signature of our call to super() is the one that gets invoked. The following code shows how we invoke a superclass constructor using arguments:


/*
  Superclass to illustrate invoking super()
*/ 
public class A { 
    private int a;

    A(int a) {
        this.a = a;        
        System.out.println("In A() constructor"); 
    }
}
/*
  Subclass to illustrate invoking super()
*/ 
public class B extends A { 
    private int b;

    B(int a, int b) {
        super(a); 
        this.b = b;        
        System.out.println("Back in B() constructor"); 
    }
}
/*
  Test class to illustrate invoking super()
*/ 
public class TestAB {
    public static void main(String[] args) {
        B b = new B(10, 20);
    }
}

We use the super(a) call as the first statement in class B to invoke this constructor in class A. This fills out part of the B() constructor which does the rest on returning.

run super constructor

The above screenshot shows the output of running our TestAB test class after compiling classes A and B.

Before we leave super comstructors behind there is one more thing you need to be aware of. Because we have made a constructor in the superclass A there is no longer a default constructor supplied by the compiler. Why should this matter to us? Well if we create another subclass of class A and don't explicitly call the super(arg) constructor the compiler puts in an implicit call to super() for us. This results in a compiler error as there isn't a no-args constructor in class A. Time to see this in action:


/*
  Superclass to illustrate implicit super() when there is no no-args constructor
*/ 
public class A { 
    private int a;

    A(int a) {
        this.a = a;        
        System.out.println("In A() constructor"); 
    }
}
/*
  Subclass to illustrate implicit super() when there is no no-args constructor
*/ 
public class C extends A { 
    private int c;

    C(int c) {
        this.c = c;        
        System.out.println("Back in C() constructor"); 
    }
}

run super constructor

The above screenshot shows the result of compiling classes A and C. We got a compiler error because the implicit call to super() supplied by the compiler didn't find a match. So the thing here is to either put a no-args constructor in the superclass, or do an explicit call to super() in the subclass.

Now we know about implicit/explicit calls to super() it should be apparent that if you mark a superclass constructor with the private access modifier, any subclass that tries to extend it will fail with a compiler error when trying to access the superclass constructor with super(), either implicitly or explicitly. This of course defeats the whole point of inheritance, but is just something to be noted.

See the Constructors lesson in the Objects & Classes section for more information on constructing our objects.

Inheritance Checklistgo to top of page Top


  • We create an inheritance hierarchy using the extends keyword wherein the subclass extends the superclass.
  • The subclass inherits the members of the superclass, assuming the access modifiers permit inheritance.
  • We can use the IS-A test to verify that the inheritance hierarchy is valid and this test works from the subclass up the inheritance chain of superclasses, but not the other way around.
  • We can override the inherited method members as long as we adhere to the following rules:
    1. A method override must have exacly the same arguments and return type (or co-variant thereof) as the superclass method with the same method name.
    2. When we override a method, the overriding method can't be less accessible.
  • We can access members from the superclass above the subclass using super.memberName.
  • We can use super() to invoke a superclass constructor and if we don't supply this explicitly, then the compiler inserts a no-args super() for us as the first statement in the constructor.
  • When explicitly coding super() we can supply arguments to invoke a constructor in the superclass matching the signature of our call.
  • When explicitly coding super() it must be the first statement within the constructor or we get a compiler error. This means we can't use super() and this() together.

Inheritance Concepts Quizgo to top of page Top

Try the quiz below to test your knowledge of this lesson

Question 1 : When using inheritance we inherit all members of the superclass?
- We inherit the members that are allowed by the <em>access modifiers</em> of the <em>superclass</em>.<br />As an example <code>private</code> members of the <em>superclass</em> will never be inherited.
Question 2 : We can override the instance variables of a superclass?
- We can only <em>override</em> the methods of a <em>superclass</em>.
Question 3 : What can we check for using the HAS-A test?
- We can check for instance variables using the <code>HAS-A</code> test.
Question 4 : What do we use the IS-A test for?
- We use the <code>IS-A</code> to check for valid inheritance.
Question 5 : The IS-A test works down the inheritance tree?
- The <code>IS-A</code> works up the inheritance tree.
Question 6 : How do we access members of the superclass above the subclass?
- We access members of the <code>superclass</code> above the <code>subclass</code> using the <code>super</code> keyword.
Question 7 : We can explicitly code super() to access what?
- We can explicitly code <code>super()</code> to access <code>superclass</code> constructors.
Question 8 : When we override a method in a subclass and we invoke that method using an instance of the subclass, what gets called at runtime?
- The overridden method in the <em>subclass</em> will be called.
Status Bar Please select an answer

What's Next?

In the next lesson we look at abstraction.

<<  Inheritance Basics                    Abstraction  >>

go to home page Java6 Tutor Homepage go to top of page Top