Chapter 37 of 57

Abstraction in Java

Imagine you are driving a car. You know how to use the steering wheel, accelerator, brake, and other controls, but you don't need to know exactly how the engine works internally every time you drive.

You simply use the controls and let the complicated internal system do its job.

This is the basic idea behind abstraction.

Abstraction means hiding unnecessary implementation details and showing only the important parts to the user.

In Java, abstraction is mainly achieved using abstract classes and interfaces.

Why Do We Need Abstraction?

Let's imagine we have a Car class. Internally, starting a car might involve many complicated operations, but the person using the car doesn't need to know all of them.

We can simply provide:

car.start();

The user knows what the method does, but doesn't necessarily need to know how it works internally.

In programming, abstraction helps us focus on what something does rather than how it does it.

Abstract Classes

Java allows us to create an abstract class using the abstract keyword.

For example:

abstract class Animal {

    abstract void sound();

    void eat() {
        System.out.println("Animal is eating.");
    }
}

Here, Animal is an abstract class.

It contains two methods:

abstract void sound();

and:

void eat() {
    System.out.println("Animal is eating.");
}

The sound() method is an abstract method. It has no implementation inside the Animal class.

It basically says:

"Every animal should have a sound, but each specific animal will decide how that sound works."

Implementing an Abstract Method

A child class can extend the abstract class and provide the implementation.

class Dog extends Animal {

    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

Now we can use the Dog class:

Dog dog = new Dog();

dog.sound();
dog.eat();

Output:

Dog barks.
Animal is eating.

The Dog class provides the implementation of the abstract sound() method, while eat() was already implemented in the parent class.

Abstract Classes Cannot Be Instantiated

An important rule is that we cannot directly create an object of an abstract class.

For example:

abstract class Animal {
}

This is not allowed:

Animal animal = new Animal(); // Error

Why? Because an abstract class may contain methods that don't have an implementation.

Instead, we create objects of concrete child classes:

Dog dog = new Dog();

The abstract class acts more like a common blueprint for its child classes.

Abstract Methods

An abstract method is declared using the abstract keyword and does not have a method body.

For example:

abstract void sound();

A child class must provide an implementation for the abstract method unless the child class is also abstract.

For example:

class Dog extends Animal {

    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

The Dog class is responsible for deciding what sound() actually does.

Abstract Class Can Have Normal Methods

An abstract class doesn't have to contain only abstract methods. It can also contain normal methods with implementations.

For example:

abstract class Animal {

    abstract void sound();

    void sleep() {
        System.out.println("Animal is sleeping.");
    }
}

A child class can then implement sound() while directly using sleep():

class Dog extends Animal {

    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

So an abstract class can provide both:

  • Methods that children must implement

  • Methods that children can directly use

A Real-World Example

Let's imagine a payment system.

Different payment methods may have different ways of processing a payment, but every payment method should have a pay() operation.

We could create:

abstract class Payment {

    abstract void pay();

    void showMessage() {
        System.out.println("Processing payment...");
    }
}

Then different payment methods can provide their own implementation:

class CreditCardPayment extends Payment {

    @Override
    void pay() {
        System.out.println("Payment made using credit card.");
    }
}

class UPIPayment extends Payment {

    @Override
    void pay() {
        System.out.println("Payment made using UPI.");
    }
}

Now:

Payment payment = new CreditCardPayment();

payment.showMessage();
payment.pay();

Output:

Processing payment...
Payment made using credit card.

The Payment class defines the general idea of a payment, while the specific child classes decide how the payment actually happens.

Abstraction vs Encapsulation

Abstraction and encapsulation are related, but they solve different problems.

Abstraction focuses on hiding unnecessary implementation details and showing only what is important.

Encapsulation focuses on protecting data and controlling how it is accessed or modified.

A simple way to remember it is:

Abstraction → Hide complexity

Encapsulation → Protect data

For example, a car's internal engine mechanism can be hidden through abstraction, while the car's internal data can be protected through encapsulation.

Abstract Class vs Normal Class

A normal class can be instantiated directly:

Animal animal = new Animal();

An abstract class cannot:

Animal animal = new Animal(); // Error

An abstract class is generally used when we want to define a common structure or behavior for related child classes while leaving some details for those child classes to implement.

The Main Idea

Abstraction is about giving the user a simple way to interact with something without exposing all of its internal complexity.

For example:

abstract class Animal {
    abstract void sound();
}

The Animal class says that every animal should have a sound() method, but it doesn't need to know exactly what every animal sounds like.

The child classes handle those details:

class Dog extends Animal {
    void sound() {
        System.out.println("Dog barks.");
    }
}

So remember:

Abstraction tells us what an object should do while hiding the details of how it does it.