What will you build

A small application that will show what is an abstract class.

What You'll Learn

What will you need

A small example with Tea, Coffee and Drinks. A class Drink will be the superclass of all the classes that represent a Drink. The sub-class will inherit all the behaviour (methods) and properties (fields) of the class Drink. I implemented a new method in the class Drink, howToPrepare().

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;
    }
    
    public String howToPrepare(){
        return null;
    }

    @Override
    public String toString () {
        return "Drink{" +
                "noun='" + noun + '\'' +
                ", price=" + price +
                ", how to prepare it "+howToPrepare()+
                '}';
    }
}

To extend a class, the keyword is "extends". The class Tea extends the class Drink. I am only redefining the constructor of the Drink class. I can use the keyword super, that will invoke the constructor of the super class. I am overriding the method howToPrepare().

public class Tea extends Drink{

    public Tea (String noun, double price) {
        super(noun, price);
    }
    @Override
    public String howToPrepare () {
        return "adding water";
    }
}

Same for the class Coffee

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

    @Override
    public String howToPrepare () {
        return "Adding water";
    }
}

And same for the class Cola

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

    @Override
    public String howToPrepare () {
        return "already made by the maker";
    }
}

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. When we are running it, thank to polymorphism, each drinks as its own how to prepare method.

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());
        }
    }
}

But you can see one problem in this implementation, the method "howToPrepareIt" for drink return null, as it is not really making any sense to have a generic method for a drink.

It is where abstract class and abstract method could be useful.

You have several reason why to implements abstract methods. When you or someone else extend an existing class, they will have the choice to override the methods that the super-class contains or not. It is sometimes important to force the other classes to override the methods contained in the super class. You will implement an abstract class to force the developper that extends the super class to override this method.

Another reason why you could use an abstract class is when the concrete implementation of a method doesn't make sense as we saw before. The method howToPrepare a drink shouldn't be a concrete method as it is forcing the developper to return a null, or an absurde value for the method implemented in the Super-class. If instead we implement it as an abstract method, we can define the signature and we will not need to return an absurde value. We can override this method in the child classes and return the value that is related to the child class.

As soon as we implement an abstract method, we will need to define the class as an abstract class.

To implement an abstract class, the keyword is "abstract".

public abstract class Drink {

An abstract class can contains both concrete and abstract methods. We will only have to create one abstract method in our previous example. The method howToPrepareIt. The class will need to be defined as abstract.

The rest of the class will be similar to the previous example.

To declare a method abstract, the signature of the method will contains the keyword abstract, and the method is only defined by the signature and doesn't have any body.

The rest of the class is the same than in the previous example.

public abstract String howToPrepare();

We will have to redefine the method in the different classes that will inherit from the class Drink (Tea, Coffee, Cola).

public class Tea extends Drink{

    public Tea (String noun, double price) {
        super(noun, price);
    }
    
    @Override
    public String howToPrepare () {
        return "adding water";
    }
}```

For the class Coffee

```java
public class Coffee extends Drink{
    public Coffee (String noun, double price) {
        super(noun, price);
    }
    
    @Override
    public String howToPrepare () {
        return "Adding water";
    }
}

and for the class Cola

public class Cola extends Drink{
    public Cola (String noun, double price) {
        super(noun, price);
    }
    
    @Override
    public String howToPrepare () {
        return "already made by the maker";
    }
}

And of course to test these classes.

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

public class TestStoreDrink {
    public static void main (String[] args) {
        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(tea);
        drinks.add(coffee);
        drinks.add(cocaCola);
        for(Drink dr: drinks){
            System.out.println(dr.toString());
        }
    }
}

You can declare a variable that will be from an abstract class, such as we did above.

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

public class TestStoreDrink {
    public static void main (String[] args) {
        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(tea);
        drinks.add(coffee);
        drinks.add(cocaCola);
        for(Drink dr: drinks){
            System.out.println(dr.toString());
        }
    }
}

The List is declaring a List that will contains variables of type Drink that is an abstract class.

However, you cannot instantiate or initialise a class Abstract, it will raise an error.

Abstract error

You can try yourself with this code:

public abstract class Person{
}

and with another class that will try to instantialise an object of type Person:

public class TestError{

public static void main(String[]args){
Person p = new Person();
	}
}

Abstraction, Encapsulation, and information Hiding

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