Inheritance

The concept of Inheritance allows us to make use the all the existing functionality of a class, without having to copy and paste it's code, whilst at the same time adding new functionality. Consider the example of the Odometer class we previously created, it allowed us to find out the number of miles that the vehicle to which it belonged had travelled. Most, but not all cars, also provide the functionality of a trip computer. This allows a driver to have an additional counter which they can reset, to track the distance from a certain point in time (for example after they have refulled).

To implement a trip computer we could consider a number of options, here are some possibilities:

Option 1 - A new class

We might create a new class, like the Odometer that has a totalMilesTravelled variable, and functions to add mileage, and retrieve the miles travelled value. The issue with this approach is that we would now need to have two objects, and when we travel, we would need to call the addMileage() method on two objects - far from ideal.

Option 2 - Add the functionality within the existing Odometer class

This cuts down on duplicate code, but it makes it difficult for our vehicle manufacturer to offer cars with different functions - they might want to charge extra for cars supplied with a trip computer.

Option 3 - Use inheritance to 'subclass' Odometer

This has the potential to work well - we can create a new class (TripComputer), which makes use of the existing code in Odometer, and simply add extra functionality. Anywhere an instance of Odometer could be used, we could use an instance of TripComputer instead.

Inheritance - a worked example

Below is the code for the Odometer class:

package uk.ac.chester;
public class Odometer {
    
    private double totalMilesTravelled;
    public double getTotalMilesTravelled(){
        return totalMilesTravelled;
    }
    public Odometer(){
        totalMilesTravelled = 0;
    }
    public Odometer(double startMileage){
        totalMilesTravelled = startMileage;
    }
    
    public void addMileage(double miles){
        totalMilesTravelled += miles;
    }    
}
To 'inherit from' or 'subclass', when we create a new class, after the class name we use the keyword extends followed by the name of the class we want to inherit from.

The syntax for our TripComputer class would appear as follows:

public class TripComputer extends Odometer {
    //code goes here    
}

The next thing to do is to consider how we can add functionality. As the trip computer's job is to show the miles travelled since a given point, we can add a variable that stores the total mileage at the point the reset button is pressed. We also need a method to handle the reset of the trip counter, and another to retrieve the trip mileage. The completed class could be written as follows:

public class TripComputer extends Odometer {

    private double tripStartMileage = 0;

    public void resetTripCounter(){
        tripStartMileage = getTotalMilesTravelled();
    }
    public double currentTripMileage(){
        return getTotalMilesTravelled() - tripStartMileage;
    }
}
It is important to note that the getTotalMilesTravelled method is called in exactly the same way we would if the methods were being added inside the Odometer class. It is possible, however to indicate explicitly that code we are using is located within the 'parent' class (referred to as the superclass). To do this, we simply prefix the method name or property value with the keyword super. In the above example we could use super.getTotalMilesTravelled() instead of just getTotalMilesTravelled(). The effect is the same, so the super keyword is redundant. Occasionally we may need to use the keyword to differentate between methods if they have been 'overridden'. Whilst this tutorial does not discuss overriding, it's worth knowing how to explicitly call a method in a superclass. (Additionally typing super followed by a full stop will present a list of methods in the parent class, which can be useful if you can't remember a method or variable name)

Using the extended class

The TripComputer class can now be used and we can call methods implemented in TripComputer or in Odometer in the same way, the example code below demonstrates that:

TripComputer tripComp = new TripComputer();
tripComp.addMileage(148.32);
System.out.println("Total mileage: "+tripComp.getTotalMilesTravelled());
System.out.println("Trip mileage: " + tripComp.currentTripMileage());
tripComp.resetTripCounter();
tripComp.addMileage(217.76);
System.out.println("Total mileage: "+tripComp.getTotalMilesTravelled());
System.out.println("Trip mileage: " + tripComp.currentTripMileage());
This code would yield the following output:
Total mileage: 148.32
Trip mileage: 148.32
Total mileage: 366.08
Trip mileage: 217.76

Constructors and Inheritance

You may have noticed that it is not possible to construct a TripComputer with a start mileage, as we can do for Odometer. Consider the code below in relation to the TripComputer we created above - we can still construct an Odometer with an existing mileage value, but not the TripComputer, so the second line will error:

Odometer basicOdometer = new Odometer(54321);
TripComputer tripComp = new TripComputer(54321);
If we do not declare any constructors for an Inherited class (such as TripComputer) then all that will be available to us is the constructor that takes no parameters. We can however call constructors from the superclass in a constructor in the inherited class.

Creating constructors and calling the superclass constructor

Constructors in an Inherited class can be created in the same way as usual, however rather than re-defining the functionality from constructors in the Parent class, we simply call whichever Constructor best fits our needs. We also need to be aware that the call to the superclass constructor must done before any other instantiation. See the example code below which allows us to instantiate a trip computer for a car which has existing mileage:

public TripComputer(double startMileage) {
    super(startMileage);
    tripStartMileage = startMileage;
In this example, the called to super will instantiate the cars entire mileage with a value (by calling the constructor in the superclass), and the second line of code will determine that that value is the start of the trip mileage.

Default Constructors

If you extend a class, if it does not have a parameterless constructor, then the inherited class must declare its own constructor(s).

Task

Using the code examples above implement the Odometer and trip computer class and test them using the Main method

Extend TripComputer so that it can calculate average mileage travelled per gallon of fuel used (mpg) over the current 'trip'. You will need to add a variable to hold fuel consumed (in litres) and a method which can be called to indicate fuel used, in litres (to be used in the same way the addMileage method would be). The new class should be named AdvancedTripComputer. Write code to test the new class.