When does an abstract class fit better than an interface, and what can it give you that an interface cannot?
An abstract class fits when the subclasses are variations of one thing that genuinely share state and share an algorithm. The template method pattern is the clearest example: the base class owns the sequence of steps, and the subclasses fill in the parts that differ.
public class Main {
public static void main(String[] args) {
new EmailReport("weekly").run();
new SmsReport("weekly").run();
}
}
abstract class Report {
protected final String name;
Report(String name) { this.name = name; }
void run() {
System.out.println("preparing " + name);
String body = build();
System.out.println("sending " + body);
}
protected abstract String build();
}
class EmailReport extends Report {
EmailReport(String name) { super(name); }
protected String build() { return "email body for " + name; }
}
class SmsReport extends Report {
SmsReport(String name) { super(name); }
protected String build() { return "sms body for " + name; }
}The output shows the shared algorithm running for both subclasses. The run method, which prepares the report and then sends it, is written once in the base class. The subclasses only supply the body, so the sequence of operations cannot drift between them, and a change to the sequence is a change in one place.
The things the base class has that an interface cannot are concrete. It holds the shared name field. Its members can be protected, so subclasses can work with inherited internals without those internals being public. It can define constructors and enforce an initialisation order, and it can chain them. And it can provide concrete helper methods that use the shared state.
The counter-argument matters too. An abstract class couples the hierarchy for life, you can only extend one, and the shared base tends to accumulate features for every subclass, which is the fragile base class problem. That is why the strong recommendation in modern Java is to reach for an interface first and to use an abstract class when the shared state and the shared algorithm are genuinely part of the concept rather than a convenience.