What will you build

You will build an application to understand the concepts of inheritance, and the concept of polymorphism

What You'll Learn

What will you need

Often, we are able to classify concepts into hierarchies, for example, a Cat is an Mammal that is an Animal. Some of the properties of an Animal are shared through all the Animal in the World (human being to a bug), they all shared some same characteristics. For example with few exceptions, animals consume organic material, breathe oxygen, are able to move, can reproduce sexually, etc.

It is almost always the case in a hierarchical classification that the nearest super class of a concept is also the nearest in term of sharing characteristics. Cats share more characteristics and behaviour with a Mammal than with an Animal. We can say that the Cats inherit the characteristics and behaviour of a Mammal, which in turn inherit the characteristics and behaviour of an Animal.

As Object Oriented paradigm is taking the real world as a base for the paradigm, it is not surprising to find inheritance in the OOP paradigm.

Inheritance in Java is the mechanism of sharing behaviour and properties. A child or sub-class will inherit all the behaviours and properties from a parent or super-class.

In java, a class can only inherit from one class (contrary at some other language such as C++).

The constructor is not inherited, and by consequence need to be implemented. However, we will see how to implement it using the keyword Super that will invoke the constructor of the parent (sub) class.

A small example with Tea, Coffee and Drinks. A class Drink will be the parent class of all the classes that represent a Drink. The child(s)-class will inherit all the behaviour (methods) and properties (fields) of the class Drink.

public class Drink {
    private String noun;
    private double price;

    public Drink (String noun, double price) {
        this.noun = noun;
        this.price = price;
    }

    public String getNoun () {
        return noun;
    }

    public double getPrice () {
        return price;
    }
    
    @Override
    public String toString () {
        return "Drink{" +
                "noun='" + noun + '\'' +
                ", price=" + price +
                " }";
    }
}

To extend a class, the keyword is "extends" followed by the class we want to inherit from. The class Tea extends the class Drink. I am redefining the constructor of the Drink class. I can use the keyword super, that will invoke the constructor of the super class.

I am also overriding the method toString to add the information from the class Tea.

public class Tea extends Drink{
    public Tea (String noun, double price) {
        super(noun, price);
    }

    @Override
    public String toString () {
        return "Tea { "  + super.toString()+ " } ";
    }
}

Same for the class Coffee

public class Coffee extends Drink{
    public Coffee (String noun, double price) {
        super(noun, price);
    }
    
    @Override
    public String toString () {
        return "Coffee { "  + super.toString()+ " } ";
    }
}

And same for the class Cola

public class Cola extends Drink{
    public Cola (String noun, double price) {
        super(noun, price);
    }
    
    @Override
    public String toString () {
        return "Cola { "  + super.toString()+ " } ";
    }

To test it, I am implementing a class TestStoreDrink. As Tea, Coffee and Cola are Drinks, we can store them in an ArrayList of Drinks.

I can store these instances of the classes Tea, Coffee and Cola in an ArrayList of Drink are they are all inheriting from the class Drink.

When we are running it, thank to polymorphism, when we are printing the information with the toString(), the method invoke in the run time is the method from the class itself.

import java.util.ArrayList;
import java.util.List;

public class TestStoreDrink {
    public static void main (String[] args) {
        Drink d = new Drink("generic drink",10.0);
        Tea tea = new Tea("English",10.0);
        Coffee coffee = new Coffee("Arabicca",15.0);
        Cola cocaCola = new Cola("CocaCola",2.0);
        List<Drink> drinks = new ArrayList<>();
        drinks.add(d);
        drinks.add(tea);
        drinks.add(coffee);
        drinks.add(cocaCola);
        for(Drink dr: drinks){
            System.out.println(dr.toString());
        }
    }
}

Tips: How to be sure that inheritance is what we want: a class that inherit from another class should always be a "is-a" concept of the super-class. For example, Cat "is a" Mammal. Tea "is a" Drink. However, a Cat has an heart.

"The dictionary definition of polymorphism refers to a principle in biology in which an organism or species can have many different forms or stages. This principle can also be applied to object-oriented programming and languages like the Java language. Subclasses of a class can define their own unique behaviors and yet share some of the same functionality of the parent class."

source: polymorphism, Retrieved February 03, 2017

When we invoke the method toString() in the previous example, it is the method refined in each of the classes that will be invoke not the method toString from the class Dink. What is happening is that in run time the Java virtual machine locates the correct method by first looking at the class of the actual object (in this case Tea, Coffee and Cola), and then calling the method with the given name in that class.

Polymorphism allows us to refine the behaviour of each classes that inherit from Drink in a transparent way for the developper using your classes.

Of course, if the method is not refined in the sub-class, it will invoke the method of the super-class like in this example.

public class GenericSubClass extends Drink{
    public GenericSubClass (String noun, double price) {
        super(noun, price);
    }
}

To test this new class, let add an instance in the ArrayList of Drink in the TestDrink class.

public class TestDrink {
        public static void main (String[] args) {
            Drink d = new Drink("generic drink",10.0);
            Tea tea = new Tea("English",10.0);
            Coffee coffee = new Coffee("Arabicca",15.0);
            Cola cocaCola = new Cola("CocaCola",2.0);
            GenericSubClass gDrink = new GenericSubClass("generic sub class",3.0);
            List<Drink> drinks = new ArrayList<>();
            drinks.add(d);
            drinks.add(tea);
            drinks.add(coffee);
            drinks.add(cocaCola);
            drinks.add(gDrink);
            for(Drink dr: drinks){
                System.out.println(dr.toString());
            }
        }
}

You will observe by running this code that the GenericSubClass is invoking the method of the super-class, as I didn't re-define a toString method in this class.

You can find the code for this example in https://git.cardiff.ac.uk/ASE_GROUP_2020/code_for_codelabs.git