The idea
What it is
Abstract classes and interfaces both let you declare a contract — a set of methods that subtypes must provide — without fully implementing them up front. Because they overlap so much, beginners often treat them as interchangeable. They aren't. The difference is about what each one is allowed to carry and how many a class can take on.
Picture a job description. An abstract class is like a partly-trained employee: they already share company knowledge and some habits (real code and stored state), and a new hire becomes one of them — you can only have one such base. An interface is like a certification — "can drive", "can swim" — a promise of a capability with no baggage attached, and you can hold many certifications at once across totally unrelated people.
The one sentence to remember
Use an abstract class when subtypes are a kind of something and should share code and state. Use an interface when unrelated types just need to promise the same capability. Is-a → abstract class; can-do → interface.
Mechanics
How it works
What an abstract class can carry
An abstract class sits halfway between a normal class and a pure contract. It cannot be instantiated on its own, but it can hold almost everything a regular class can:
- Fields / instance state — it can store data, like a
nameor a cached value, that every subclass inherits. - A constructor — it can run setup code when a subclass object is built (subclasses call
super(...)). - Concrete methods — fully written methods that all subclasses get for free, plus
abstractmethods that subclasses are forced to fill in.
The catch: in most languages a class can extend exactly one abstract class. You inherit a single base, so it's the right home for a shared identity plus shared machinery.
What an interface can carry
An interface is a leaner thing — a contract of method signatures with (classically) no bodies and no stored state. A class implements an interface by providing real code for every method it names. The superpower is multiplicity: a single class can implement many interfaces at once, mixing capabilities from completely unrelated contracts.
// Interface: just the capability, no state, no constructor.
interface Drawable {
draw(): void; // a signature — the WHAT, no HOW
}
// One class can promise MANY unrelated capabilities at once:
class Widget implements Drawable, Serializable, Clickable {
draw() { /* ... */ }
serialize() { /* ... */ }
onClick() { /* ... */ }
}Modern languages blurred the line a little: since Java 8, interfaces can carry default methods (a shared body) and static methods — yet they still hold no instance state. TypeScript interfaces describe object shapes and are purely structural (they vanish at runtime). Python uses abc.ABC for abstract base classes and Protocol for interface-style structural typing. C++ has no separate interface keyword at all — an interface there is just an abstract class whose methods are all pure virtual (= 0).
The decision rule
When both seem to fit, ask two questions. Is there shared code or state every subtype should inherit, and is this an "is-a" relationship? → reach for an abstract class. Do unrelated types just need to advertise the same ability, with each writing its own version? → reach for an interface. And when you need both — shared state and the freedom to mix several capabilities — the idiomatic answer is to combine them: an abstract base for the shared part, interfaces for each extra capability.
Why "single base, many contracts" matters
Single inheritance keeps the shared-state story simple — there's exactly one base to reason about, so no ambiguity over whose name field you inherited. Interfaces sidestep that problem by carrying no state at all, which is why a language can safely let you implement as many as you like.
Interactive prototype
See it. Build it. Break it.
A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.
About this simulation
Two columns — Abstract Class and Interface — laid out side by side, each row showing what it can and can't do. Flip the requirement toggles on top to describe your situation, and watch the recommended column light up with a → use this badge while the log explains why. Ask for shared state and mixing several contracts at once, and it tells you to combine both.
Hands-on
Try these yourself
Open the prototype above, predict what happens, then verify.
Read the comparison grid
Before touching anything, scan the two columns. Notice where the badges differ: holds state and constructor are ✓ for the abstract class but ✗ for the interface, while how many reads one vs many. The row that's identical — forces a contract — is ✓ for both, which is exactly why they get confused.
Toggle a requirement and watch the pick
Flip I need to share common code on. The Abstract Class column lights up with a → use this badge and the log explains why — concrete methods live on a base, not on a pure interface. Now turn it off and flip I just need a capability contract; the recommendation jumps to the Interface column live.
Force the conflict
Turn on I need stored fields/state and one class must mix several of these at the same time. No single tool wins — so the log advises the combined pattern: an abstract base for the shared state plus interfaces for the extra capabilities. That's the real-world answer when requirements pull both ways.
In practice
When to use it — and what trips people up
Picking between the two
Default to an interface — it's the looser, more composable choice, and types that share nothing but a capability can still implement it. Promote to an abstract class the moment you find yourself wanting to share real code or stored state across subtypes that genuinely form an "is-a" family. When both pulls are real at once, don't choose — use an abstract base for the shared part and layer interfaces on top for the mix-in capabilities.
Don't force an is-a where there's only a can-do
Reaching for an abstract base just to share one helper method drags every subtype into a single inheritance line they may not belong in. If the types aren't truly the same kind of thing, an interface (or composition) keeps them free.
What it gives you
- Reach for an abstract class when subtypes share an is-a relationship — a
Circleand aSquareare both genuinely a kind ofShape. - Reach for an abstract class when you have concrete code or stored state every subtype should inherit, like a shared
namefield and adescribe()method. - Reach for an abstract class when you want a constructor to enforce valid setup for the whole family of subtypes.
- Reach for an abstract class when single inheritance is fine and you want one clear, central base to evolve over time.
Common mistakes
- Reach for an interface when unrelated types need the same ability — a
FileButtonand aPlayercan both beClickablewithout sharing a family. - Reach for an interface when one class must mix several capabilities at once, since a class can implement many but extend only one.
- Reach for an interface when you want a pure contract with no state — just signatures callers can depend on, each type writing its own version.
- Reach for an interface when you're designing for maximum flexibility and testing, letting any type opt in to a capability without inheritance baggage.
Reference
Code & further reading
A minimal reference implementation and pointers worth bookmarking.
// Abstract class: shared state + a concrete method + an abstract one.
abstract class Shape {
constructor(protected name: string) {} // stored state + constructor
describe(): string { // concrete — shared by all shapes
return `${this.name} with area ${this.area().toFixed(2)}`;
}
abstract area(): number; // the WHAT — each shape fills in
}
// Interface: a pure capability contract, no state.
interface Drawable {
draw(): void;
}
// Circle IS-A Shape (extends one base) and CAN-DO Drawable (implements).
class Circle extends Shape implements Drawable {
constructor(private r: number) {
super("circle");
}
area(): number {
return Math.PI * this.r * this.r;
}
draw(): void {
console.log(`drawing ${this.describe()}`);
}
}
const c = new Circle(2);
console.log(c.describe()); // circle with area 12.57
c.draw();References & further reading
4 sources- Docsdocs.oracle.com
Oracle — Abstract Methods and Classes
The canonical Java tutorial on abstract classes, abstract methods, and when to choose them over an interface.
- Docsdocs.oracle.com
Oracle — Defining an Interface
How interfaces work in Java, including default methods (Java 8) and implementing many at once.
- Docsdocs.python.org
Python docs — abc (Abstract Base Classes)
Declaring abstract bases in Python with abc.ABC and @abstractmethod — the abstract-class side of the comparison.
- Articleen.wikipedia.org
Wikipedia — Interface (object-oriented programming)
A language-agnostic overview of interfaces and how they relate to abstract classes across OOP languages.
Knowledge check
Did it land?
Quick questions, answers revealed on submit. Nothing is scored or saved.
question 01 / 04
Which capability belongs to an abstract class but NOT to a classic interface?
question 02 / 04
A Widget needs to be drawable, serializable, and clickable — three unrelated capabilities. What's the natural fit?
question 03 / 04
Since Java 8, interfaces can include default methods with real bodies. Does that make them the same as abstract classes?
question 04 / 04
You need subtypes to share a stored name field AND mix in several unrelated capabilities. What's the idiomatic design?
0/4 answered