Abstract Class vs. Interface — What’s the Real Difference?

Abstract Class vs. Interface — What’s the Real Difference?

Both promise a set of actions before any details are filled in. Both stop you from creating an object directly from them. So why do object-oriented languages bother offering both? Here is what genuinely separates them, and how to know which one fits a given situation.

01

The Big Idea, in One Breath

An abstract class ties expectations to a shared identity and some shared groundwork. An interface ties them purely to a shared ability, with no assumptions about identity at all.

Imagine you are organising a talent show. You could describe the requirements in two very different ways. The first way: “Every performer must be a Student at this school, and every Student already knows our school song and our stage bow, but can add their own special act on top.” The second way: “Anyone — student, teacher, visiting parent — can join, as long as they can perform for at least three minutes on stage.” Notice the difference: the first description ties performers to a specific identity they must already belong to. The second description only cares about one specific ability, no matter who or what possesses it.

That difference is essentially the whole story behind abstract classes and interfaces in object-oriented programming. Both are ways of setting expectations before any real, detailed code gets written — but an abstract class ties those expectations to a shared identity and some shared groundwork, while an interface ties them purely to a shared ability, with no assumptions about identity at all.

Everyday Analogy

Think of an abstract class like being born into a family — you automatically inherit the family name, some shared traits, and a bit of a head start, whether you asked for it or not. Think of an interface like signing up for a swim class — anyone can join, regardless of family background, as long as they are willing to learn to swim. Both connect you to a group, but in very different ways.

This guide is the fourth in a small series exploring the core ideas of object-oriented programming, following earlier guides on the four pillars, inheritance, and polymorphism. Abstract classes and interfaces both build directly on those earlier ideas — they are really just two different, deliberate ways of setting up the promises that make polymorphism work smoothly. Understanding the difference between them is not about memorising a rulebook; it is about recognising which kind of relationship actually exists between the pieces of a program, and choosing the tool that describes that relationship honestly.

02

What Is an Abstract Class?

A partial blueprint. Some behaviour is fully finished and inherited exactly as written; some is deliberately left unfinished for each child class to complete in its own way.

An abstract class is a class that cannot be used to directly create an object on its own — it exists purely as a partial blueprint for other classes to build upon. It can contain a mix of two things: fully finished behaviour that every child class automatically inherits exactly as written, and unfinished behaviour — sometimes called abstract methods — that every child class is required to complete in its own way.

Everyday Analogy

Picture a partially filled-in school worksheet handed out by a teacher. The name field, the date field, and the instructions at the top are already printed — nobody needs to rewrite those. But the answer blanks are deliberately left empty, because the teacher genuinely wants each student to fill those in with their own specific answer, not a copied, identical one.

i
In plain words

An abstract class is like a half-finished recipe card: some steps are already written out in full, ready to follow exactly as given, while other steps just say “fill this part in yourself,” leaving room for each cook to finish it their own way.

Because an abstract class can carry real, working code alongside its unfinished parts, it is a natural fit whenever a family of related classes shares genuine, reusable groundwork — not just a shared promise, but shared, working logic they would otherwise have to duplicate.

It helps to picture abstract classes as sitting one step above ordinary classes and one step below interfaces, in terms of how much they actually finish for you. A regular class is fully complete and ready to use immediately. An abstract class is deliberately left partway done, on purpose, because the people designing it know that certain details genuinely depend on which specific child class eventually fills them in — and forcing a single, one-size-fits-all answer at that point would be a mistake. An interface, which the next section covers, takes this idea even further, leaving almost everything unfinished except the promise itself.

03

What Is an Interface?

A pure promise — a checklist of abilities. No opinion about how any of them are actually implemented, no shared ancestry required. Unrelated classes can adopt the same interface freely.

An interface is a pure promise — a list of actions that any class choosing to adopt it must provide, without offering any actual working code of its own (in many languages, at least in its traditional, classic form). Where an abstract class says “here’s some code, plus a few blanks to fill in,” an interface says “here’s a checklist of abilities you must have; how you build them is entirely up to you.” An interface has no idea, and no opinion, about how any of its promised actions actually get implemented — it only cares that they exist and behave sensibly when called.

i
In plain words

An interface is like a job posting that lists required skills — “must be able to drive, must be able to lift 20kg” — without specifying anything about who the applicant is or what family, background, or history they come from. Any qualified applicant, from any walk of life, is welcome to apply.

Because an interface makes no assumptions about identity or shared ancestry, a single class is typically allowed to adopt several different interfaces at once — after all, a person can easily hold several unrelated skills at the same time, the same way a class can promise several unrelated sets of abilities.

It is worth pausing on why this “no assumptions about identity” quality matters so much in practice. A Duck and an Airplane share almost nothing in common — one is a living creature, the other a machine — yet both can genuinely promise to “fly.” Forcing them into a shared abstract class purely so they could both fly would be strange and dishonest; a Duck simply is not an Airplane, and an Airplane is not a kind of Duck. An interface sidesteps this problem entirely, letting both adopt a shared “Flyable” promise without pretending they belong to the same family at all.

04

The Core Differences, Side by Side

With both definitions in hand, five practical differences settle most day-to-day design decisions between them.

With both definitions in hand, it is worth laying the two side by side and comparing them point by point. This table captures the differences that tend to matter most in everyday, practical decision-making.

QuestionAbstract ClassInterface
What relationship does it express?“is-a” — a shared identity“can-do” — a shared ability
Can it contain finished, working code?Yes, alongside unfinished partsTraditionally no (some modern languages now allow limited exceptions)
How many can one class extend or adopt?Usually just oneUsually several at once
Can it hold shared data or properties?YesGenerally no, beyond fixed constants
What is it best suited for?A family of closely related classes sharing real logicUnrelated classes that all need to promise the same specific ability
Abstract Class shared identity + real, working code Interface shared ability, no code a checklist to fulfil A Real Class extends one, implements many
A class typically extends one abstract class but can implement several interfaces at once.

Notice how the diagram mirrors real family life again: a person has exactly one set of parents (their identity, like extending one abstract class), but can pick up any number of separate skills and certifications over a lifetime (their abilities, like implementing several interfaces). Nobody finds it strange that someone can be, at the same time, a licensed driver, a certified lifeguard, and a fluent speaker of two languages — those are independent, stackable abilities, not competing identities.

05

Seeing Them in Code

A small, concrete vehicles example makes the distinction click — and reveals how the two cooperate rather than compete.

Definitions and tables are useful, but nothing makes the distinction click quite as clearly as a small, concrete example. Let us look at one built around vehicles, which shows the two side by side and reveals exactly how they cooperate rather than compete.

Example — an abstract class with shared, working code
abstract class Vehicle:
    def __init__(self, brand):
        self.brand = brand

    def honk(self):
        return "Beep beep!"   # fully finished, shared by all

    abstract def fuel_type(self):
        pass   # unfinished — every child must complete this

Every vehicle honks the same way, so that behaviour is written once, fully finished, inside the abstract class. But each vehicle uses a different fuel, so fuel_type() is left unfinished, forcing every child class to fill it in for itself.

Example — an interface as a pure promise
interface Rechargeable:
    def charge(self):
        pass   # just a promise, no code at all

class ElectricCar(Vehicle, Rechargeable):
    def fuel_type(self):
        return "Electricity"
    def charge(self):
        return f"{self.brand} is charging."

Notice that ElectricCar extends the single Vehicle abstract class — inheriting its shared identity and its ready-made honk() behaviour — while separately adopting the Rechargeable interface, promising a completely unrelated ability that has nothing to do with being a vehicle at all. A toy, a phone, or a flashlight could just as easily adopt that same Rechargeable interface, without needing to be any kind of vehicle whatsoever.

It is worth tracing through what happens if a new GasCar class joins this same family. It would also extend Vehicle, automatically inheriting honk() for free and being required to fill in its own fuel_type(), just like ElectricCar did. But unless a gas car genuinely needs to be rechargeable — which, in the real world, it does not — there is no reason to force it to adopt the Rechargeable interface at all. This selective, opt-in quality is one of the most practically useful things interfaces offer: related classes can still meaningfully differ in which extra abilities they choose to carry.

06

Can a Class Use Both at Once?

Yes — and in real, working software, this combination is extremely common. One abstract class for identity, several interfaces for extras.

Yes, and in real, working software, this combination is extremely common. A single class frequently extends one abstract class to gain a shared identity and some ready-made groundwork, while also adopting one or more interfaces to promise additional, unrelated abilities on top.

Identity

One abstract class

Provides the “is-a” relationship, shared fields, and any finished, reusable code.

Abilities

Several interfaces

Each one promises a specific, focused ability the class chooses to support.

Returning to the earlier example: ElectricCar is-a Vehicle (identity, through the abstract class), and it can-do Rechargeable (ability, through the interface). A GasCar would also be a Vehicle, but would not need to adopt Rechargeable at all — showing how interfaces let unrelated classes opt into shared abilities selectively, without forcing every member of a family to carry every ability.

This layered combination — one abstract class for the backbone, several interfaces for the extras — is such a common pattern in professional software that it is worth internalising as a default mental model. Think of the abstract class as answering “what fundamentally is this thing?” and each interface as answering “what, additionally, can this particular thing also do?” A well-designed system usually keeps these two questions cleanly separated, rather than letting one sprawling abstract class try to answer both at once.

07

Why Bother Having Both?

Each is optimised for a different job. Abstract classes for genuine code reuse among close relatives. Interfaces for flexible, cross-cutting promises among strangers.

A fair question at this point is: if interfaces are so flexible, why do languages bother offering abstract classes at all? The honest answer comes down to what each one is optimised for. Abstract classes are optimised for genuine code reuse among closely related things — they let a whole family of classes share real, working logic without repeating it. Interfaces are optimised for flexible, cross-cutting promises — they let completely unrelated classes agree to support the same ability, without pretending to share a family they do not actually belong to.

If a language offered only interfaces, every single class would need to reimplement shared logic from scratch, since interfaces traditionally cannot hold working code — a genuine loss of the convenience abstract classes provide. If a language offered only abstract classes, every class needing a particular ability would be forced into a single, rigid family tree, even when that ability had nothing to do with the class’s actual identity — a genuine loss of the flexibility interfaces provide. Offering both lets a developer reach for whichever tool actually fits the relationship in front of them, rather than being stuck bending one tool to do a job it was not designed for.

This same reasoning is why so many well-designed systems settle into the layered pattern already described: one abstract class establishing a stable core identity, and a small collection of focused interfaces layered on top for whichever additional abilities a particular class actually needs. It is less a compromise between two competing tools and more a genuine partnership, each covering the exact gap the other was never meant to fill.

08

How to Choose Between Them

One honest question settles most decisions cleanly: is this relationship genuinely about shared identity, or purely about a shared ability?

A simple, honest question usually settles the choice: is this relationship genuinely about shared identity, or purely about a shared ability? It is tempting to look for a stricter, more mechanical rule, but in practice, this one question resolves the vast majority of real design decisions cleanly.

1

Ask the “is-a” question first

Would it make sense to say “X is-a Y”? A SportsCar is-a Vehicle. If that sentence rings true, an abstract class is likely the right fit.

2

Check for shared, reusable code

If several related classes already need the exact same working logic, not just the same promise, an abstract class lets that logic live in one place.

3

Ask the “can-do” question next

Would it make more sense to say “X can-do Y”? A Duck can-do Fly, and so can an Airplane, despite having nothing else in common. That points toward an interface.

4

Consider how many “families” might need it

If the ability might be needed by completely unrelated classes across a program, an interface avoids forcing an artificial shared ancestry just to gain that one ability.

i
A useful rule of thumb

Many experienced developers summarise this as: use an abstract class to share code among closely related things, and use an interface to promise a capability across otherwise unrelated things.

There is also a helpful “smell test” worth mentioning for trickier, borderline cases. If describing the relationship out loud requires stretching the truth a little — “well, I guess a Duck could sort of be considered a type of Airplane, in a loose sense, since they both fly” — that stretching is usually a signal that an interface is the more honest choice. Genuine “is-a” relationships tend to sound obviously true the moment they are spoken aloud, without any hesitation or awkward qualifiers needed.

09

Pros and Cons of Each

Neither is universally “better.” Each brings real strengths and real trade-offs, depending on which relationship is actually being modelled.

Neither abstract classes nor interfaces are universally “better” — each brings real strengths and real trade-offs, and a fair evaluation depends entirely on which relationship is actually being modelled.

Abstract Classes

Strengths

  • Lets related classes share real, working code, avoiding repetition.
  • Can hold shared data and fields, not just method promises.
  • Clearly expresses a genuine, stable “is-a” family relationship.

Trade-offs

  • A class can usually only extend one abstract class, limiting flexibility.
  • Forces a shared ancestry even when only a single ability is actually needed.
  • Changes to the abstract class can ripple across every dependent child.

Interfaces

Strengths

  • A class can adopt several interfaces at once, offering great flexibility.
  • Works cleanly across completely unrelated classes and families.
  • Keeps promises separate from implementation, encouraging looser, more flexible design.

Trade-offs

  • Traditionally offers no shared, working code, so some duplication can creep in.
  • Cannot hold shared data fields the way an abstract class can.
  • A very large interface with many required actions can become burdensome to fully implement.

A useful way to hold both lists in mind at once: an abstract class trades some flexibility for the convenience of shared, ready-made code, while an interface trades that shared convenience for much greater flexibility in how many different promises a single class can hold at once. Neither trade is free, which is exactly why real systems tend to lean on both, each where it genuinely fits best.

10

Common Pitfalls to Avoid

Three recurring traps — forcing an abstract class for reuse, bloating an interface with unrelated ideas, and assuming interfaces can never contain any code at all.

Even developers who understand the theory well can fall into a handful of recurring traps when applying it to real, evolving codebases.

Reaching for an abstract class purely to reuse code

If two classes only share a little logic but have no genuine “is-a” relationship, forcing them into a shared abstract class just to avoid retyping a few lines tends to create an awkward, artificial hierarchy. Composition, covered in an earlier guide in this series, is often the more honest fix in that situation. A good warning sign: if the shared abstract class starts accumulating methods that only some of its children actually use in a meaningful way, that is usually a hint the identity relationship was never fully genuine.

Designing an interface that is really several unrelated ideas glued together

An interface that promises ten completely unrelated actions forces every adopting class to implement all ten, even the ones that do not genuinely fit. Splitting an overloaded interface into several smaller, focused ones usually leads to a cleaner, more flexible design. This idea even has its own name in professional circles — the “interface segregation principle” — which essentially argues that no class should be forced to depend on abilities it does not actually use.

Assuming interfaces can never contain any code at all

Some modern languages now allow interfaces to include limited, optional default behaviour for convenience, blurring the classic, strict distinction slightly. It is worth checking the specific rules of whichever language is actually being used, rather than assuming older, stricter definitions apply everywhere. This does not erase the core conceptual difference described throughout this guide — it just means the mechanical boundary between the two has softened somewhat in certain modern languages.

!
Watch out for

Choosing an abstract class simply because it “feels more powerful,” without checking whether a genuine shared identity actually exists. Power is not the deciding factor — the real relationship between the classes is.

11

Where You Will Meet These Every Day

Both quietly shape a huge amount of the software behind everyday apps, from media libraries and smart homes to game engines — even a hospital staff directory.

Both abstract classes and interfaces quietly shape a huge amount of the software running behind everyday apps, even though the people using those apps never see the underlying structure directly.

Everyday ExampleHow It Maps
A media library appSong, Podcast, and Audiobook might all extend an abstract “AudioItem” (shared identity, shared playback code) while separately implementing a “Downloadable” interface (a shared ability, not every audio type needs).
A smart home appLight, Thermostat, and Lock might all implement a “RemoteControllable” interface, despite having almost nothing else in common as devices.
A game engineKnight and Wizard might extend an abstract “Character” (shared health, shared identity) while both implementing a “Swimmable” interface that only some characters need.
1

Abstract class per class

Typically, a class extends exactly one abstract parent — the single, stable identity it belongs to.

Many

Interfaces a class can adopt

The same class can freely stack any number of separate, focused abilities on top of its identity.

2

Questions to ask

Is-a? Points to an abstract class. Can-do? Points to an interface. Most design calls come down to those two.

Beyond code

A hospital staff directory

Roles group people by identity; certifications tag them by cross-cutting abilities — the same pattern, outside software.

It is genuinely worth noticing how often this exact pattern repeats outside of software as well. A hospital staff directory might group people by their formal role — Doctor, Nurse, Administrator — much like an abstract class groups related classes by shared identity. But it might also tag people by cross-cutting abilities — “Certified in CPR,” “Speaks Spanish,” “Available for night shifts” — much like interfaces, since those tags apply across roles rather than belonging to just one. Recognising this pattern outside of code is often what finally makes the programming version feel less abstract and more like plain common sense.

12

Questions People Often Ask

Seven honest questions that come up over and over — and honest answers to each.

Can an abstract class have zero unfinished methods?

In most languages, yes — an abstract class only needs to be explicitly marked as abstract to prevent direct object creation; it is not strictly required to contain any unfinished methods at all. That said, most abstract classes do include at least one unfinished method, since that is usually the whole reason for making them abstract in the first place.

Why cannot you create an object directly from an abstract class?

Because it is genuinely incomplete — some of its promised behaviour was deliberately left unfinished, waiting for a child class to fill in the blanks. Creating an object straight from an abstract class would mean having an object that does not actually know how to do everything it claims to promise.

Do interfaces and abstract classes solve the same underlying problem?

They are both aimed at describing structure and expected behaviour before writing full implementation details, and both prevent creating an object directly from them. But they solve genuinely different problems underneath that surface similarity — one is about shared identity and reusable code, the other is purely about a shared, adoptable ability.

Which one should a beginner learn first?

Neither strictly needs to come first, but many learners find abstract classes slightly easier to grasp initially, since they build directly on inheritance, which is usually taught earlier. Interfaces tend to click once there is a real need to give unrelated classes a shared, promised ability — a need that becomes obvious the first time a project actually calls for it.

Is one of these considered outdated compared to the other?

No — both remain genuinely useful, actively used tools in modern object-oriented design, and most real, professional codebases use a healthy mix of both, chosen based on the actual relationship being modelled rather than personal habit or trend.

What happens if a class forgets to fill in something an abstract class or interface requires?

Most languages catch this early, often before the program even runs, refusing to accept a class that claims to extend an abstract class or implement an interface without actually completing every required piece. This early check is one of the quiet, practical benefits both tools offer: incomplete promises get caught close to the moment they were made, rather than surfacing as a confusing error much later, deep inside a running program.

Can an abstract class implement an interface too?

Yes, and this is actually a common, useful pattern. An abstract class can adopt an interface and even partially fulfil some of its required actions using real, working code, while deliberately leaving other required actions unfinished for its own child classes to complete. This lets a whole family of related classes share both an identity and a partially completed ability at once.

13

Key Takeaways

Not a rulebook to memorise. Both are simply honest ways of writing down what kind of relationship actually exists between the pieces of a program.

Both abstract classes and interfaces exist to describe expectations before all the details are filled in — the real difference lies in what kind of relationship each one is built to express. Neither one is a rulebook to be memorised and mechanically applied; both are simply honest ways of writing down something true about how the pieces of a program relate to each other, so that the code itself communicates that relationship clearly to the next person who reads it.

Remember this

  • An abstract class expresses a shared identity — an “is-a” relationship — and can include real, working code alongside unfinished parts.
  • An interface expresses a shared ability — a “can-do” relationship — traditionally offering only promises, no working code.
  • A class usually extends just one abstract class but can implement several interfaces at once.
  • Abstract classes suit closely related families sharing real logic; interfaces suit unrelated classes that all need the same specific ability.
  • The two can, and often should, be combined: one abstract class for identity, several interfaces for additional abilities layered on top.
  • The deciding question is simple: is this genuinely “is-a,” or is it really just “can-do”?

Leave a Reply

Your email address will not be published. Required fields are marked *