implements vs Abstract Classes
Two ways to enforce a contract on a class, and when each earns its keep.
Both implements and extends abstract make a class honour a contract, but they differ in what they carry.
implements an interface
implements is a pure compile-time check: the class must have the required members, but inherits nothing. A class can implement many interfaces at once.
interface Serializable { toJSON(): string }
interface Comparable<T> { compareTo(o: T): number }
class Money implements Serializable, Comparable<Money> { /* must supply both */ }extends an abstract class
An abstract base can ship real code and state down to subclasses, but a class extends exactly one. It is single inheritance plus shared implementation.
How to choose
- Need to guarantee a shape only, and mix several? Use
implementswith interfaces. - Need to share concrete behaviour or protected state? Use an abstract base.
They are not rivals β a class often extends one abstract base and implements a couple of interfaces.
Example
When to use it
- A team uses implements when multiple unrelated classes must satisfy the same interface without sharing any implementation.
- A template-method pattern uses an abstract class to provide a shared algorithm with abstract hook methods for specialisation.
- A plugin system uses implements to allow third-party classes to satisfy a Plugin interface without inheriting any base class.
More examples
implements interface β no shared code
Two unrelated classes implement the same interface; they share a contract but no implementation.
interface Drawable {
draw(): void;
}
class Circle implements Drawable {
draw(): void { console.log('Drawing circle'); }
}
class Square implements Drawable {
draw(): void { console.log('Drawing square'); }
}
function render(shapes: Drawable[]): void {
shapes.forEach((s) => s.draw());
}Abstract class β shared code + contract
Abstract class provides the shared print() implementation and mandates generate(); subclasses add specific content.
abstract class Report {
abstract generate(): string[]; // must implement
print(): void {
this.generate().forEach((line) => console.log(line));
}
}
class SalesReport extends Report {
generate(): string[] { return ['Total: $1200', 'Units: 40']; }
}
new SalesReport().print();Class that extends and implements
A class can extend an abstract base and implement an interface simultaneously, combining both patterns when both are needed.
interface Serializable { serialize(): string; }
abstract class Entity {
abstract id: number;
}
class Product extends Entity implements Serializable {
id = 1;
name = 'Pen';
serialize(): string {
return JSON.stringify({ id: this.id, name: this.name });
}
}
Discussion