What is an Abstract Class in Object Oriented Programming

Abstract class concept is one of the basic concepts of Object-Oriented Programming. It gives us the possibility of modelling concepts from the real world and facilitates the use of one of the OOP principles: code reuse.

An abstract class in Object-Oriented Programming (OOP) is a class that cannot be instantiated. In other words, you cannot create an object using the abstract class.

Ok, but what can I do with this? When should I use an abstract class?

Let’s find out what are possible scenarios where we can use abstract classes.

Why do we use an abstract class

Abstract classes are closely related to the concept of inheritance in OOP. If you are not sure of what inheritance is, you should first read this post so you can understand what an abstract class is and why we need them in OOP.

Remember that before starting coding, we should first design what we want to code.

An important part of our design is to identify classes and the relationships among them (compositions, aggregations, use, and inheritance).

At this time, we can identify two (or more) classes that inherit from a common class.

As an example, let’s imagine we are modelling a situation where we must calculate employee salaries. But we must model two types of employees: part-time and full-time employees.

As you should understand, the company calculates the salary for these two types of employees in a different way. However, they still have some attributes and functionalities in common: name, surname, id, salary, etc.

Salary is something that should be calculated for every employee. Therefore, we should have a method to calculate a salary in our base class Employee. In this way, both types of employees will inherit this method.

Notice that although an employee has a salary, we need to know the type of employee (part-time or full-time) to calculate it.

This is a typical case of why we need abstract classes. In this case, the method salary must be abstract in the class employee and have the respective implementation in the subclasses according to the type of employee.

Find below a more detailed example related to employees’ salaries.

Abstract class example in Java

This is the most common use case. It is also one of the most common use cases when teaching/learning inheritance.

As you should remember, OOP is about modelling situations from real life.

Let’s say we have two types of employees in a certain company: admin and service employees. Admin employees have a salary that is calculated according to the level of their position. If position is 1 then the salary is 5000 and if it is 2 the salary is 8000. For the service employees, we calculate the salary as basic salary + 6% of the basic salary.

Notice that this is a simplified example.

So, how can we model this situation using OOP?

Abstract class Employee in Java

From the description above, we can identify three classes: Employee, “AdminEmployee” and “ServiceEmployee”.

Notice that all “AdminEmployee” is an Employee and all “ServiceEmployee” is an Employee. This means that both, “AdminEmployee” and “ServiceEmployee” inherits from “Employee”. If you are not sure about how to identify inheritance,  read the post following this link.

Our main aim is to calculate the salary of the employees. But notice, that the salary calculation depends on the type of employee (Admin or service).

This means that in the class Employee, we cannot calculate the salary because we don’t know how to do it. We don’t have all the information that we need, in this case, the type of employee.

Let’s see how the implementation will look like. I’m going to use Java for the examples, but the logic behind it is the same for other programming languages.

abstract class Employee{
    protected String name;

    public Employee(String name) {
        this.name = name;
    }

    abstract public float Salary();
}

This is our class Employee. In this example, because we don’t know how to calculate the salary, the method salary should be declared abstract. In other words, we cannot implement the method at that level of abstraction.

If we have one class with an abstract method, then the class becomes abstract. In the case of Java, we must explicitly declare the class as abstract by using the keyword abstract before the class declaration (abstract class Employee). It might differ in other programming languages, but in all of them, a class with an abstract method is also abstract.

Now, what can we do in this abstract class?

Using the abstract class Employee

The way we use abstract classes is through inheritance.

Let’s see the implementation of the class “AdminEmployee”.

class AdminEmployee extends Employee{
    private Position position;

    public AdminEmployee(String name, Position position) {
        super(name);
        this.position = position;
    }

    @Override
    public float Salary() {
        float salary = 0;
        switch (this.position){
            case ONE: salary = 5000;
            case TWO: salary = 8000;
        }
        return salary;
    }
}

As you can see, the previous class inherits from our abstract class.

Notice we add a new attribute because is a characteristic of the type of object this class represents. Next, we must implement the constructor, so we have a way to initialize new objects of this type.

The next fact to look at is that now we know how to calculate the salary for the type of employee that this class represents: admin employees.

According to our problem description: ” Admin employees have a salary that is calculated according to the level of their position. If position is 1 then the salary is 5000 and if it is 2 the salary 8000″. Using that description, we implement (override) the method “Salary” in the new class.

Now, this class is not abstract anymore and we can use it to create objects as follows.

AdminEmployee admin1 = new AdminEmployee(“John”, Position.ONE);

The next class we must implement is the class “ServiceEmployee”.

class ServiceEmployee extends Employee {
   private float basicSalary;

    public ServiceEmployee(String name, float basicSalary) {
        super(name);
        this.basicSalary = basicSalary;
    }

    @Override
    public float Salary() {
        return basicSalary + 6*basicSalary/100;
    }
}

In this case, we also have a new attribute according to the problem statement: basic salary.

So, we proceed in the same way, we add the attribute and implement the constructor to be able to initialize new objects using this class.

Next, we implement (override) the method “Salary” for this new class. Notice that we calculate the salary for a “ServiceEmployee” in a different way than for “AdminEmplyees”. So, we reflect that difference in the implementation of the method.

The implementation above is a classic usage of an abstract class. We use them to model abstractions, that have operations (methods) that we cannot implement at that level of abstraction. Then we go down to another level of abstraction by implementing a new class that inherits from the abstract class. At this new level of abstraction, we know how to implement the operations and we do so.

In an abstraction hierarchy, we can have as many abstract classes as we need. We can inherit from an abstract class and not implement abstract methods. Therefore, our new class will also be abstract.

Abstract class without abstract methods in Java

In Java, you can declare an abstract class, without the need to have an abstract method.

This will have the effect that you cannot create objects using that class because it is abstract.

This is a mechanism that you can use we you want to make sure that no one can create instances of that class.

Although, in my opinion, is an unnecessary mechanism, it is an option that some programming languages provide.

So, if we are modelling a situation that we can justify the use of an abstract class without abstract methods, the programming languages gives us a way to implement that model.

Abstract classes vs Interfaces

An interface in programming helps in designing common functionalities that have to be implemented by certain classes.

Informally, you can think about an interface in Java as a class without attributes and any method implementation.

A great example to show how we use interfaces is the interface Comparable in Java (IComparable in C#).

This interface has only one method.

public interface Comparable<T> {
    /**
     * Compares this object with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object
     **/
    public int compareTo(T o);
}

Any programming language knows how to compare integers, but when it comes to other types of objects, for instance, students, we should tell the computer how we compare two students.

Any class that implements the interface Comparable, will implement the method compareTo. In this method, we will define when one object is greater than another one.

So, in short, we use an abstract class when designing/implementing a hierarchy of classes and in one of them, we don’t know how to implement a certain method. On the other hand, we usually use an interface, when we need classes to implement a common behaviour with disregard of whether they are in the same hierarchy (one inherits from the other one) or not.

Abstract class Implementing an interface in Java

In some cases, we can use abstract classes and interfaces together.

Let’s go back to the example of the employees. Imagine we want to be able to compare two employees and we will say one employee is greater than the other one if the name goes first alphabetically (“Ana” is greater than “Bob”).

In that case, we should design the class Employee as follows.

abstract class Employee implements Comparable<Employee>{
    protected String name;
   
    public Employee(String name) {
        this.name = name;
    }

    abstract public float Salary();
}

In the example below, you can see that the abstract class Employee implements the interface Comparable.

From this point, we have to choices. We can implement the methods defined in the interface Comparable in this class Employee, or we can implement them in the child classes: ServiceEmployee and AdminEmployee.

Because we want to compare two employees according to their name, and the name is in the class Employee, we have all we need to implement the method in the class Employee. Our class Employee will then be implemented as follows.

abstract class Employee implements Comparable<Employee>{
   protected String name;

   public Employee(String name) {
       this.name = name;
   }

   abstract public float Salary();

    @Override
    public int compareTo(Employee o) {
        return name.compareTo(o.name);
    }
}

The type String in Java also implements the interface Comparable. Therefore, we can use the method compareTo implemented in the class String to compare two strings.

In Java, sometimes we use interfaces to “model” multiple inheritance.

Abstract class in Python

Find below the python implementation for our previous example of the employees.

from abc import ABC, abstractmethod

class Employee(ABC):
    def __init__(self, name):
        self.name = name
    @abstractmethod
    def salary(self):
        pass


class ServiceEmployee(Employee):
    def __init__(self, name, basic_salary):
        super().__init__(name)
        self.basic_salary = basic_salary
    def salary(self):
        return self.basic_salary + 6*self.basic_salary/100;


class AdminEmployee(Employee):
    def __init__(self, name, position):
        super().__init__(name)
        self.position = position
    def salary(self):
        if self.position == 1:
            return 5000
        else: return 8000

Abstraction and Abstract classes

Abstract classes are also related to abstractions.

When we use an abstract class, we are stating a certain level of abstraction.

In the case of the example of the employees, when we define the class Employee, we are “hiding” or “not considering” at that moment what specific type of employee we are modelling (that is our level of abstraction).

At that level of abstraction, we model everything common to all employees (they all have a name and a salary) and we postpone more specific details.

When we define the class AdminEmployee, we refine the level of abstraction by adding how the salary is calculated. However, some characteristics are not included at that level of abstraction because we don’t need them to solve our problem. One example is what are the benefits of this employee, the retirement age, the medical aid, etc.

As you can see, abstract classes and fully implemented classes (classes that are not abstract) are both abstractions. We usually need both types of classes in Object-Oriented Programming.

Summary

Abstract classes are very tight to the use of inheritance (and polymorphism).

It is a useful mechanism that facilitates code-reuse and helps you create models that are more accurate and represent better certain situations from real life.

Related topics:

H@ppy coding!