Inheritance - ConceptsJ8 Home « 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 superclass tree to help illustrate the points of this lesson:

inheritance 5

When we last visited Vehicle Town, we had subclassed the Vehicle class and things were looking good. But the mayor has been in touch and 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.

inheritance 7



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

inheritance 8



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

IS-A and HAS-A Relationships 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 relationship, which we use to check inheritance, anywhere where you would use the extends keyword just replace it with IS-A and see how the relationship looks.

For the HAS-A relationship, which we use to check composition, 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
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
  • invalid
Car IS-A Vehicle - yes.
Car HAS-A Vehicle - no.
Sink extends Kitchen
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
Bird IS-A Canary - no.
Bird HAS-A Canary - no.
Reversed
Canary IS-A Bird - yes.
Canary HAS-A Bird - no.
Window extends House
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 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.

inheritance 9
Figure 1. IS-A and HAS-A.

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 subclass 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.


package info.java8;
/*
  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." );
    }
}

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


package info.java8;
/*
  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"); 
    }
}

run HGV class
Screenshot 1. Running the TestHGV 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 subclass 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.


package info.java8;
/*
  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);
    }
}

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


package info.java8;
/*
  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"); 
    }
}

run Garbage class
Screenshot 2. Running the TestGarbage 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 Invocation 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
Figure 2. Inheritance Method Invocation.

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.

Related Quiz

OO Concepts Quiz 6 - Inheritance - Concepts

Lesson 6 Complete

In this lesson we continued our investigation of Java inheritance and conceptualized where our objects fitted into a class hierarchy using IS-A and HAS-A relationships.

What's Next?

We continue our investigation of inheritance by looking at accessing instance variables, methods and constructors in our superclasses using the super keyword.