Abstract Classes
Partial blueprints — classes that capture shared behavior but leave specific details to their subclasses
Sometimes the class you want to write is almost complete, but one
or two parts have to be filled in by each subclass differently. The
Shape class in the previous page is a good example: it has a
describe() method that works for everyone, but area() is
meaningless for "shape in general." Each subclass has to fill it in.
Java models this with the keyword abstract. An abstract
class is a class that may have:
- Normal fields and concrete methods (just like any class).
- Abstract methods — methods declared without a body, leaving subclasses to provide the implementation.
You cannot construct an abstract class directly with new. You can
only construct concrete subclasses of it.
A first abstract class
The * on area() and the <<abstract>> stereotype mark them as
abstract. Concrete subclasses must implement area().
The describe method in Shape calls area(). At the time Shape
is written, nobody knows what area() will return — that depends
entirely on which concrete subclass is plugged in. This is exactly
what makes abstract classes powerful: the parent provides the
skeleton, the child fills in the steps.
Why "abstract"?
The class is "abstract" in the same sense that "vehicle" is abstract in everyday language. You can't point at a vehicle that is just a vehicle — every vehicle you encounter is some concrete kind: a bicycle, a car, a tram. "Vehicle" is a useful category, but it has no realisation of its own.
The Template Method idea
A classic use of abstract classes is the Template Method pattern
(one of the GoF originals). The parent class defines the overall
shape of an algorithm in a concrete method and leaves the variable
steps as abstract.
Notice the final on file() — subclasses cannot override the
shape of the algorithm. They can only fill in the marked
extension points. That is the spirit of Template Method: a tightly
controlled invitation to vary.
Abstract class vs. concrete class
| Concrete class | Abstract class |
|---|---|
Has no abstract methods | Has at least one abstract method, or is itself declared abstract |
Can be instantiated with new | Cannot be instantiated; you must subclass |
| Implements every member it declares | Leaves at least one method to subclasses |
| Decision: "I know how to do everything" | Decision: "I describe the shape; subclasses fill in the variable parts" |
When not to use an abstract class
The next page is about interfaces, which solve a similar problem (describing a shape) from a different angle. The short version:
- Use an abstract class when there is genuine shared state and/or shared implementation across the subclasses that you want to reuse.
- Use an interface when you want to describe only a set of capabilities and have many unrelated classes implement them.
Many modern OOP designs use interfaces by default and reach for abstract classes only when there's real shared code to factor out.
Practice
Build a tiny notification framework.
Notificationis an abstract class:- Constructor takes a
String recipient. - A concrete final method
send()returns the string"To " + recipient + ": " + body(). - An abstract protected method
body()that returns the message body. EmailNotificationextendsNotification. Its constructor also takes aString subject.body()returns"Subject: " + subject.SmsNotificationextendsNotification. Its constructor also takes aString text.body()returns thetextas-is.
Expected output:
To ada@oop.dev: Subject: Welcome!
To +1-555-0100: Your code is 1234
Test your understanding
Which is true about an abstract class in Java?
It cannot have any concrete methods
It is the same as an interface
It can have both concrete and abstract methods, and cannot be instantiated with new directly
It can only have static methods
Why does the Template Method pattern often mark its top-level method final?
To make it run faster
To prevent subclasses from changing the overall shape of the algorithm; they may only fill in the marked steps
Because the JVM forbids overriding abstract methods
To make the class implicitly abstract
When should you prefer an abstract class over an interface?
When you want many unrelated classes to share a tiny contract
When you have genuine shared state and concrete code that all subclasses would otherwise duplicate
Whenever you have more than three methods
When you don't want to use @Override