OOP Concepts · Polymorphism in Depth

Abstract Class vs Interface

Abstract classes and interfaces both say “subclasses must provide X”. The difference: an abstract class is a shared parent; an interface is a capability any class can add.

Intermediate 6 min Lesson 18 of 38 Runnable examplesExamples use modern JavaScript (ES2022 private fields) and C# 13.
0/38 in OOP Concepts
On this page · 6 min · Intermediate

In simple words#

An abstract class is a family business you join (one parent, shared assets). An interface is a skill certificate (you can hold many). Choose by asking: “is-a” family, or “can-do” skill?

Abstract class
One base, can share code and state.
Interface
Many allowed, contract only (mostly).
Rule of thumb
Interface for capability, abstract class for shared base.

Learning objectives#

  • List the key differences
  • Use the is-a vs can-do rule to choose
  • Combine both in one design
  • Answer the interview question with an example

Explanation#

Abstract class: can have fields, constructors and implemented methods. A class can extend only ONE. Use it for a family of closely related types that share code — “is-a”.

Interface: a contract of members, with no state. A class can implement MANY. Use it for capabilities that unrelated types can share — “can-do”.

Rule of thumb: Car is a Vehicle (abstract class). Car can be insured, a House can be insured (interface IInsurable).

They work together: class Car : Vehicle, IInsurable. Vehicle provides shared driving logic; IInsurable adds a capability.

Since C# 8, interfaces can include default method bodies, which blurs the line a little — but they still cannot hold instance fields, and the is-a / can-do rule still guides the choice.

Syntax#

C# Illustrative
public abstract class Vehicle(string regNo)        // is-a: shared state + code
{
    public string RegNo => regNo;
    public int Km { get; private set; }
    public void Drive(int km) => Km += km;
    public abstract int ServiceInterval { get; }
    public bool ServiceDue => Km >= ServiceInterval;
}


public interface IInsurable                         // can-do: a capability
{
    decimal InsuranceValue();
}


public class Car(string regNo) : Vehicle(regNo), IInsurable
{
    public override int ServiceInterval => 10000;
    public decimal InsuranceValue() => 800000m;
}


public class House : IInsurable                     // unrelated, same capability
{
    public decimal InsuranceValue() => 5000000m;
}

Code example: Vehicles share code; insurable things share a capability#

JavaScriptVehicles share code; insurable things share a capability Runnable
// Abstract class: shared state and code for a family of related types
class Vehicle {
  constructor(regNo) {
    if (new.target === Vehicle) throw new Error('Vehicle is abstract');
    this.regNo = regNo;
    this.km = 0;
  }
  drive(km) { this.km += km; return this; }
  serviceDue() { return this.km >= this.serviceInterval(); }
  serviceInterval() { throw new Error('abstract'); }
}


class Car extends Vehicle {
  serviceInterval() { return 10000; }
  insuranceValue() { return 800000; }       // also "implements" Insurable
}


class Truck extends Vehicle {
  serviceInterval() { return 5000; }
}


// Interface: a capability unrelated types can share
class House {
  insuranceValue() { return 5000000; }
}
const isInsurable = obj => typeof obj.insuranceValue === 'function';


const car = new Car('MH12AB1234').drive(6000);
const truck = new Truck('KA01XY9999').drive(6000);
console.log(car.serviceDue(), truck.serviceDue());


const things = [car, truck, new House()];
const insurable = things.filter(isInsurable);
console.log(insurable.length, insurable.reduce((sum, t) => sum + t.insuranceValue(), 0));
Expected output
false true
2 5800000
Opens this example in the full playground

Step-by-step explanation#

  1. Vehicle is the abstract parent: Car and Truck share km, drive() and serviceDue().
  2. Each vehicle fills in its own serviceInterval().
  3. insuranceValue() is a capability: Car has it, Truck does not, and House — not a vehicle at all — has it too.
  4. The insurance code filters by capability, not by class, so it works across unrelated types.

Real-world use case#

In .NET, Stream is an abstract class (FileStream, MemoryStream share buffering code), while IDisposable is an interface implemented by streams, database connections and HTTP clients alike.

Common mistakes#

Using an abstract class just to force methods

If there is no shared code, you are spending your one base-class slot for nothing. Use an interface.

Using an interface when code is copy-pasted into every implementation

If five classes repeat the same logic, that logic belongs in a shared base class (or a helper they use).

Answering only with rules in interviews

Give a concrete example: Vehicle (abstract) vs IInsurable (interface).

Best practices#

  • Default to interfaces for dependencies and capabilities
  • Use abstract classes for related types with shared code
  • Combine both: one base class, several interfaces
  • Keep both small

Remember this#

Abstract class = IS-A + shared code. Only one.

Interface = CAN-DO capability. As many as you like.

Car : Vehicle, IInsurable — use both together.

Practice exercise#

Your task: Model a Bird (abstract class with name and a shared describe()) and a Flyer capability (fly()). Create Sparrow (bird + flyer), Penguin (bird only) and Drone (flyer only, not a bird). Log which of the three can fly.

Need a hint?
Check the capability with typeof obj.fly === "function".
Show solution
JavaScript Runnable
class Bird {
  constructor(name) { this.name = name; }
  describe() { return `${this.name} is a bird`; }
}
class Sparrow extends Bird { fly() { return 'flap flap'; } }
class Penguin extends Bird {}
class Drone { constructor() { this.name = 'Drone'; } fly() { return 'whirr'; } }


const all = [new Sparrow('Sparrow'), new Penguin('Penguin'), new Drone()];
console.log(all.filter(x => typeof x.fly === 'function').map(x => x.name));
// [ 'Sparrow', 'Drone' ]
Opens this example in the full playground

Mini quiz#

Abstract Class vs Interface quizMultiple choice Question 1 of 3

Which can hold instance fields (state)?

Score so far: 0/0

Want more? Practise all OOP Concepts MCQs · OOP Concepts interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.