Module 8: Object-Oriented Design in Practice
Abstract Classes and Interfaces
Learn how to choose between a shared base class and a shared contract without over-designing your class model.
Author
Java Learner Editorial Team
Reviewer
Technical review by Java Learner
Last reviewed
2026-04-17
Java version
Java 25 LTS
Learning goals
- Know when an abstract class is the better fit
- Know when an interface keeps designs flexible
- Avoid mixing both without a clear reason
Before you start
- You are comfortable creating classes, objects, methods, and constructors
Why this matters: Intermediate Java is where design starts to matter more than syntax. The wrong abstraction can lock you into brittle hierarchies fast.
Use an abstract class when subclasses share real code or state: An abstract class is helpful when several related types need common fields, helper methods, or a shared workflow.
Use an interface when you want a capability, not a family tree: Interfaces are better when unrelated classes need the same contract, such as Payable, Searchable, or Exportable.
Good rule: If your first thought is shared state, lean abstract class. If your first thought is shared behavior from different kinds of objects, lean interface.
Runnable examples
One shared base class plus one capability interface
abstract class Employee {
private final String name;
protected Employee(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract double monthlyPay();
}
interface Reportable {
String buildReport();
}
class Developer extends Employee implements Reportable {
private final double salary;
Developer(String name, double salary) {
super(name);
this.salary = salary;
}
@Override
public double monthlyPay() {
return salary;
}
@Override
public String buildReport() {
return getName() + " ships code.";
}
}Expected output
Developer objects share Employee state and also satisfy the Reportable contract.
Common mistakes
Using inheritance just to share one helper method
Prefer composition or a utility/helper class when there is no true shared abstraction.
Putting lots of unrelated default behavior in one interface
Keep interfaces small and capability-focused so implementations stay clear.
Mini exercise
Create an abstract `Shape` class with an abstract `area()` method, then add a `Drawable` interface with a `draw()` method.
Summary
- Abstract classes are best for shared state or shared implementation.
- Interfaces define capabilities across unrelated types.
- Good design starts with clear intent, not with trying to reuse every line of code.
Next step
Next, compare composition against inheritance so you can spot when subclassing is the wrong move.
Sources used