Interfaces and abstract classes (covered next chapter) both define contracts that other classes must fulfill, and beginners often struggle to know when to use which.
| Interface | Abstract class | |
|---|---|---|
| Implementation | No method bodies (traditionally); some modern languages allow default methods | Can mix implemented and unimplemented methods |
| State (properties) | Traditionally no instance properties; only method signatures and constants | Can have properties with real values |
| Multiple inheritance | A class can implement many interfaces | A class can typically extend only one abstract class |
| Relationship modeled | 'Can-do' — a capability, unrelated to what the class fundamentally is | 'Is-a' — a genuine categorical relationship |
| Use case | Defining a capability shared by otherwise-unrelated classes | Sharing common code among closely related classes |
A practical rule of thumb:
- Use an interface when you're describing a capability that unrelated things might share:
Comparable,Serializable,Playable. ADocumentand aPlaylistmight both beSortable, despite having nothing else in common. - Use an abstract class when you're describing a category of genuinely related things that share substantial common implementation:
Vehicleas a base forCar,Truck, andMotorcycle, which likely share real logic likecalculateFuelEfficiency().
In practice, the two are often combined:
interface Drawable {
function draw(): void
}
abstract class Shape implements Drawable {
protected color
function __construct(color) { this.color = color }
// Shared, concrete implementation for all shapes
function describe(): string {
return "A " + this.color + " shape with area " + this.area()
}
abstract function area(): number // still must be implemented by each subclass
}
class Circle extends Shape {
function __construct(color, radius) { super(color); this.radius = radius }
function area(): number { return 3.14159 * this.radius ** 2 }
function draw(): void { print("Drawing a circle") }
}
Here Shape provides shared implementation (describe()) while Drawable defines a capability any shape (or anything else) could implement — combining the strengths of both tools.