Abstraction in Java with Example Program

You must understand Polymorphism with example, before you get into details of abstraction.

Abstraction means, skipping implementation details of behavior by expressing an entity in a generic way, leaving details to be defined by subclasses. In simple words, we define classes that contain only method prototypes but not the body of the methods, the methods' body is defined by subclasses. This approach help to process objects of subclasses polymorphically. In Java, abstraction is achieved using abstract classes and interfaces.

In real life, when we plan something, first we think in terms of what needs to be done, then we go into details of how it should be done. In same way, in abstraction, we think, what behavior an entity should have and only declare that behavior (but not define it). Then, in each specific concept (or subclass), we define that behavior i.e. 'how' a specific class should act.

For example, consider a student in academic system, identify common behavior that each student must have e.g. requestLeave, submitAnApplication, withdrawCourse, etc. but detail of each process (or behavior) may be different for different types of students. We declare list of processes in Student class without providing the detail of how each process shall be performed, the details (how of that behavior) are defined in subclasses of Student e.g. RegularStudent, EveningDegreeStudent, etc. This approach help to process different type of Student objects polymorphically. Our Student class shall look like this:

public abstract class Student {
    public abstract void requestLeave(LeaveRequest leaveRequest);
    public abstract void submitAnApplication(Application application);
    public abstract void withdrawCourse(Course course);
}

You can also define instance attributes and non-abstract methods in an abstract class. Lets assume we want to describle a Car entity with some instance attributes, non-abstract methods and some behavior that each car should have, leaving the implementation detail to be defined by specific cars i.e. subclasses of Car. The behavior may inlcude changing gear, increasing or decreasing speed, stopping the car, changing oil, put the front lights ON, starting and stoping vipors etc. Each specific car shall define each behavior as per its own requirements or way e.g. StandardCar, SportsCar, HybridCar, etc. The definition of Car shall look like this:

public abstract class Car {

    private String make;
    private int modelYear;

    public Car(String make, int modelYear) {
        this.make = make;
        this.modelYear = modelYear;
    }

    public String getMake() {
        return make;
    }

    public void setMake(String make) {
        this.make = make;
    }

    public int getModelYear() {
        return modelYear;
    }

    public void setModelYear(int modelYear) {
        this.modelYear = modelYear;
    }

    public abstract void changeGear();
    public abstract void increaseSpeed();
    public abstract void decreaseSpeed();
    public abstract void stopCar();
    public abstract void changeOil();
    public abstract void onFrontLights();
    public abstract void startStopViper();
}

You see, for all abstract methods, we only provided the prototype. The body of each method would be defined by subclasses of Car.

Lets consider another and last example in more detail. If we are building a software that handles different type of shapes e.g. Triangle, Circle, and Rectangle etc. and we need to define some method (or behavior) that calculate the area of the shape. You see, the behvabior is common i.e. all shapes has some area but how that behavior shall be defined (or how the area shall be calculated) is different for each Shape type. We can define Shape class as follows:

public abstract class Shape {
    public abstract double calculateArea() ;
}

Making a method abstract means, no valid definition exist at this level of generalization. If a class contains an abstract method, that class must be declared as abstract too, as we did in above code listing. We can't create objects of abstract classes because they contain some behvaior whose definition do not exist.

When we defined calculateArea() method in Shape class in Polymorphism in Java with Example, we returned 0 in it. But you see, returning 0 do not make sense or is not the perfect solution. The better solution is not defining the method body at all. Below I explain why defining empty method or returnng 0 is wrong approach for such parent classes e.g. Shape.

Benefits of Declaring a Method as Abstract 

Object oriented design help to detect many errors at compile time. If we declare calculateArea() as non-abstract in Shape but forget to override in subclass Trianlge, compiler would not generate an error like "you missed to override the calculateArea method in Triangle class", but accept the code as valid. At runtime, when calculateArea would be called for Triangle object, parent class method would be called that return 0, which is definitely invalid.

If a method in parent class is abstract, compiler enforce the subclasses to must override it, otherwise, subclass shall be declared as abstract. In other words, if calculateArea method is abstract in Shape class and we define a subclass Triangle, its mandatory for Triangle class to define the body of calculateArea() method, otherwise, we must declare Triangle as abstract, to make its code compilable.

That means, a developer can't forget to override a method when overriding is necessory for system to produce correct output. We achieve this, by making our class and method abstract.

Lets see the listing, below code generates compiler error becasue we missed to override the calculateArea() method (which is mandatory, because calculateArea is abstract in Shape class).

public class Triangle extends Shape {

    private double base;
    private double height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    public double getBase() {
        return base;
    }

    public void setBase(double base) {
        this.base = base;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
}


It produce following compile-time error:

Error: Trianlge is not abstract and does not override abstract method calculateArea() in Shape

You see by making calculateArea() abstract in Shape class, we have enforced to all (non-abstract) subclasses to must override the method to make the code compilable. If we add calculateArea() definition in Triangle class defined above, it would compile successfully. Here the method definition:

@Override
    public double calculateArea() {
        return (height * base) / 2;
    }

Why We Declare Abstract Methods, What If We Don't

You may think, if we are not defining body of calculateArea() method in Shape class, then isn't a better idea to remove this method altogether from Shape class and only define in subclasses of Shape? The answer is: NO. If we removed the calculateArea() from Shape class, we can't call this method from Shape type variable, that means, we can't call calculateArea() polymorphically, as we did in Painter class. See again:

public class Painter {
    final static int RATE = 50;
    public double calculatePaintingCost(Shape shape) {
        return shape.calculateArea() * RATE;
    }
}

If we remove the calculateArea method from Shape class, then we can't call calculateArea from shape variable, as we did in above code listing. Compiler check the variable type (and not the object type) to decide whether the method call is valid? If the method is defined or declared in that type e.g. Shape, the method call is considered legal, otherwise, compiler raises a error.

I hope, now you understand why we can't create object of abstract classes and how abstract methods in parent class force subclasses to provide the definition of those methods and what benfits this rule brings.

Comments