Inheritance allows us to use an existing class as a basis for a new, related class. Imagine we wanted to write classes representing different types of vehicle, e.g. Car, Bus, Motorbike. If we were to write the three classes entirely separately, we'd be repeating a good deal of code - e.g. the code for starting and stopping the engine is common to all three classes.
So what we could do in this case is create a Vehicle class, containing common functionality for all types of vehicle, and then inherit various subclasses of Vehicle (such as Car, Bus, and Motorbike) which provide additional functionality specific to that type of Vehicle. We can say that:
In Java, we use the keyword extends, e.g.
public class Car extends Vehicle { .... Car attributes and methods }This means that the Car class inherits from Vehicle. All attributes and methods in Vehicle will be inherited by Car, so when you create a Car, all the Vehicle methods and attributes will be available.
Below is a Vehicle superclass with Bike and Car subclasses.
// Generic Vehicle superclass public class Vehicle { // Attributes common to all vehicles protected int topSpeed, nWheels; protected String make; public Vehicle (String makeIn, int topSpeedIn, int nWheelsIn) { this.make=makeIn; this.topSpeed=topSpeedIn; this.nWheels=nWheelsIn; } public void move() { System.out.println("Moving along..."); } public void print() { System.out.println( "Make : " + make + "\nTop speed: " + topSpeed + "\nno. wheels: " + nWheels); } }
public class Car extends Vehicle { // Car-specific data private int engineCapacity; private boolean engineRunning; public Car(String makeIn, int topSpeedIn, int engineCapacityIn) { // Call the superclass (Vehicle) constructor to construct the Vehicle // component of the Car. We will pass in the top speed, make, and // number of wheels (which we know will be 4) super(makeIn, topSpeedIn, 4); // Set up the Car-specific attributes (engine capacity) this.engineCapacity = engineCapacityIn; } // Overridden move() for cars public void move() { if (engineRunning) { System.out.println("Driving along..."); } else { System.out.println ("Can't drive the car if the engine's stopped!!!"); } } // Car-specific methods public void start() { engineRunning=true; } public void stop() { engineRunning = false; } // Convert the car to a String. Note the use of super.toString() to // call the superclass (Vehicle) version of toString() to get the // make, top speed and number of wheels. public void print() { super.print(); System.out.println(" Engine running? " + engineRunning + "\nEngine capacity: " + engineCapacity); } }
public class Bike extends Vehicle { // Bike-specific data private boolean isOffRoad; private int nGears; public Bike(String makeIn,int topSpeedIn,boolean isOffRoadIn,int nGearsIn) { //Call the superclass (Vehicle) constructor to construct the Vehicle // component of the Bike. We will pass in the top speed, make, and // number of wheels (which we know will be 2) super(makeIn,topSpeedIn,2); // Set up the Bike-specific attributes (off-road status and gears) this.isOffRoad = isOffRoadIn; this.nGears = nGearsIn; } public void print() { super.print(); System.out.println(" Off road? " + isOffRoad + "\nNo. Gears: " + nGears); } } public class InheritanceTestApp { public static void main (String args[]) { Car c= new Car("Ford",120,2000); Bike b = new Bike("Raleigh",30,true,27); c.print(); b.print(); c.move(); c.start(); c.move(); c.stop(); b.move(); } }
How is this working?
super(make, topSpeed, 4)The super keyword represents the superclass. When used in this way, it means "call the superclass constructor". So when the Car constructor is called, it in turn calls the Vehicle constructor to initialise the Vehicle aspects of the car (make, top speed, and number of wheels). The Vehicle constructor will initialise the corresponding attributes to the values passed to super(): because we always pass 4 for the number of wheels, the nWheels attribute will always be initialised to 4 for Cars.
In the example, Vehicle and Car both have a method called move(). We have overridden the original version in Vehicle with the version in Car - in other words, replaced the original Vehicle version of move() with a new version in Car. This will mean that:
In the same way, print() is overridden in both Car and Bike. However, unlike for move(), the overridden version of print() calls the original, Vehicle version of print() using the super keyword: super.print();.
Return to your chase game from topic 7. Create a new class, Creature, that contains all the attributes and methods common to both Hero and Monster. Make Hero and Monster inherit from Creature. Rewrite the game to use the inheritance hierarchy.