Chapter 38 of 57

Abstract Classes in Java

An abstract class is a class that is designed to be used as a base class for other classes. It allows us to define common properties and methods while leaving some behavior for child classes to implement.

We create an abstract class using the abstract keyword.

For example:

abstract class Animal {

}

This looks like a normal class, but there is an important difference: we cannot directly create an object of an abstract class.

Animal animal = new Animal(); // Error

Instead, another class can extend the abstract class:

class Dog extends Animal {

}

Now we can create a Dog object:

Dog dog = new Dog();

Why Do We Use Abstract Classes?

Imagine we are creating a program for different types of animals. Every animal has some common behavior, such as eating, but different animals make different sounds.

We can put the common behavior inside an abstract class:

abstract class Animal {

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

Then create specific child classes:

class Dog extends Animal {

    void bark() {
        System.out.println("Dog is barking.");
    }
}

Now Dog automatically gets the eat() method from Animal.

Dog dog = new Dog();

dog.eat();
dog.bark();

Output:

Animal is eating.
Dog is barking.

The abstract class gives the child classes common functionality.

Abstract Methods

An abstract class can also contain abstract methods.

An abstract method is a method that is declared but does not have an implementation.

For example:

abstract class Animal {

    abstract void sound();
}

Here, sound() doesn't contain any code. It simply says that every child class must provide its own version of sound().

A child class can implement it:

class Dog extends Animal {

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

And another child class can provide a completely different implementation:

class Cat extends Animal {

    @Override
    void sound() {
        System.out.println("Cat meows.");
    }
}

Now:

Dog dog = new Dog();
Cat cat = new Cat();

dog.sound();
cat.sound();

Output:

Dog barks.
Cat meows.

The abstract class defines what the child classes should provide, while the child classes decide how to implement it.

Abstract Class Can Have Normal Methods

An abstract class can contain both abstract and normal methods.

For example:

abstract class Animal {

    abstract void sound();

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

Here, sound() is abstract, while eat() already has an implementation.

A child class must implement sound():

class Dog extends Animal {

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

But it can directly use eat():

Dog dog = new Dog();

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

Output:

Dog barks.
Animal is eating.

This is one of the useful features of abstract classes. They can provide some common functionality while leaving other parts for child classes.

Abstract Class Can Have Variables

An abstract class can also have variables just like a normal class.

abstract class Animal {
    String name;

    abstract void sound();
}

A child class can use the inherited variable:

class Dog extends Animal {

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

Now:

Dog dog = new Dog();

dog.name = "Buddy";
dog.sound();

Output:

Buddy barks.

So an abstract class can contain variables, normal methods, abstract methods, and constructors.

Abstract Class Can Have Constructors

Even though we cannot create an object directly from an abstract class, it can still have a constructor.

For example:

abstract class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    abstract void sound();
}

A child class can call the parent constructor:

class Dog extends Animal {

    Dog(String name) {
        super(name);
    }

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

Now:

Dog dog = new Dog("Buddy");

dog.sound();

Output:

Buddy barks.

The Animal constructor initializes the name, even though the actual object is a Dog.

Abstract Class vs Normal Class

A normal class can be instantiated:

class Animal {
}

Animal animal = new Animal();

An abstract class cannot:

abstract class Animal {
}

Animal animal = new Animal(); // Error

An abstract class is generally used when the class represents a general concept and we expect specific child classes to provide the actual implementations.

For example, Animal is a general concept, while Dog and Cat are specific types of animals.

Abstract Class vs Interface

Abstract classes and interfaces are both used to achieve abstraction, but they are not exactly the same.

An abstract class can contain:

  • Instance variables

  • Constructors

  • Normal methods

  • Abstract methods

For example:

abstract class Animal {

    String name;

    Animal(String name) {
        this.name = name;
    }

    void eat() {
        System.out.println("Eating...");
    }

    abstract void sound();
}

Interfaces are designed differently and are commonly used to define a contract that classes agree to follow. We'll cover interfaces separately, so for now focus on understanding what an abstract class does.

A Complete Example

Let's put the concepts together:

abstract class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

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

    abstract void sound();
}

class Dog extends Animal {

    Dog(String name) {
        super(name);
    }

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

class Main {
    public static void main(String[] args) {

        Dog dog = new Dog("Buddy");

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

Output:

Buddy is eating.
Buddy barks.

Here, Animal provides the common structure, while Dog provides the specific implementation of sound().

The main thing to remember is:

An abstract class is a base class that cannot be instantiated directly and can contain both implemented methods and abstract methods that child classes must implement.