An abstract class is a class that cannot be instantiated directly and can leave operations unimplemented.
In the previous article we created a hierarchy where Car inherited from Vehicle. Everything worked fine: we could create both new Car() and new Vehicle().
But stop and think for a second. Does it make sense to do new Vehicle()?
If you go to a dealership and say “I want to buy a vehicle”, the salesperson will ask you “Which one? A car, a motorcycle, a truck?”. You cannot buy a plain “vehicle”. The vehicle is an abstract concept, a category, not a concrete physical object.
The same thing happens in programming. Sometimes we have parent classes that serve to group common logic, but that should never be instantiated because they are incomplete.
That’s what Abstract Classes are for.
Differences: Abstract vs Concrete
| Feature | Concrete Class | Abstract Class |
|---|---|---|
| Instantiation | Can do new. | Forbidden to do new. |
| Methods | All must have a body (code). | Can mix normal and abstract methods. |
| Usage | Define real objects. | Define base templates and partial contracts. |
| Inheritance | Can be final (leaf of the tree). | Designed to be inherited. |
What is an Abstract Class?
An abstract class is a class that cannot be instantiated. It only serves as a base for other classes to inherit from.
It is defined with the keyword abstract.
// Abstract class definition
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// CONCRETE method (Has code and works)
// All animals sleep the same way, so we implement it here.
public void sleep() {
System.out.println("Zzzzz... " + name + " is sleeping.");
}
}If you try to do this in your main, the compiler will stop you:
// Animal a = new Animal("Bug"); // COMPILATION ERROR!
// 'Animal' is abstract; cannot be instantiatedWhen to use abstract classes?
- You have a clear “Is-A” relationship (A Dog is an Animal).
- You want to share code: You have common logic (like
sleep()ormove()) that you don’t want to repeat in each child. - There are behaviors you cannot define generically: You know what the children must do, but not how.
- You want to forbid generic instances: Prevent someone from creating objects that should not exist on their own.
If you only have abstract methods (no shared code or attributes), then you probably don’t need an Abstract Class, but an Interface. That’s exactly what we’ll see later.
Abstract Methods: “Signing a Contract”
The real power of abstract classes is not just forbidding new. It is the ability to define Abstract Methods.
An abstract method is a method that has no body (no curly braces {}). It only has the definition (the name and parameters).
public abstract class Animal {
// ... attributes and constructor ...
// ABSTRACT METHOD (No body, ends with semicolon)
public abstract void makeSound();
}Implementation Requirement
If a class has at least one abstract method, the entire class MUST be declared abstract.
Implementing Abstraction
Now, when we create the child classes (Dog, Cat), Java will force us to implement the code for makeSound(). If we don’t, the code won’t compile.
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
// MANDATORY: Override the abstract method
@Override
public void makeSound() {
System.out.println("Woof Woof!");
}
}
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println("Meow!");
}
}Polymorphism with Abstract Classes
This is where everything fits together. Although we cannot do new Animal(), we can use Animal as a reference type to store its children.
// Parent reference (Abstract) = Child instance (Concrete)
Animal myPet = new Dog("Firulais");
myPet.sleep(); // Uses shared code in Animal
myPet.makeSound(); // Uses Dog-specific code ("Woof")This allows us to have, for example, an Animal[] zoo and iterate over it calling makeSound() for all of them, regardless of whether they are dogs, cats, or lions. Each one will sound correctly.