Labs ICT
โญ Pro Login

Inheritance & Polymorphism

How objects inherit behavior and how polymorphism enables flexible code.

Inheritance โ€” Code Reuse Through Hierarchy

Inheritance allows a class to acquire the attributes and methods of another class. The new class (subclass/child) inherits from an existing class (superclass/parent), gaining all its properties and optionally overriding or extending them. This promotes code reuse and establishes a natural hierarchy.

class Animal {
  constructor(name, species) {
    this.name = name;
    this.species = species;
    this.energy = 100;
  }

  eat(food) {
    this.energy += food.calories;
    console.log(`${this.name} ate ${food.name}. Energy: ${this.energy}`);
  }

  sleep(hours) {
    this.energy += hours * 20;
    console.log(`${this.name} slept for ${hours} hours. Energy: ${this.energy}`);
  }

  describe() {
    return `${this.name} is a ${this.species}`;
  }
}

// Dog inherits from Animal
class Dog extends Animal {
  constructor(name, breed) {
    super(name, "Canine");
    this.breed = breed;
    this.tricks = [];
  }

  learn(trick) {
    this.tricks.push(trick);
    console.log(`${this.name} learned "${trick}"!`);
  }

  perform(trick) {
    if (this.tricks.includes(trick)) {
      console.log(`${this.name} performs "${trick}"! ๐Ÿ•`);
    } else {
      console.log(`${this.name} doesn't know "${trick}" yet.`);
    }
  }

  describe() {
    return `${this.name} is a ${this.breed} dog`;
  }
}

// Cat inherits from Animal
class Cat extends Animal {
  constructor(name, isIndoor) {
    super(name, "Feline");
    this.isIndoor = isIndoor;
    this.happiness = 50;
  }

  pet() {
    this.happiness += 10;
    console.log(`${this.name} purrs happily! Happiness: ${this.happiness}`);
  }

  describe() {
    return `${this.name} is a ${this.isIndoor ? "indoor" : "outdoor"} cat`;
  }
}

const rex = new Dog("Rex", "German Shepherd");
rex.eat({ name: "bone", calories: 50 });
rex.learn("sit");
rex.learn("shake");
rex.perform("sit");    // Rex performs "sit"!
rex.perform("jump");   // Rex doesn't know "jump" yet.

const whiskers = new Cat("Whiskers", true);
whiskers.eat({ name: "tuna", calories: 30 });
whiskers.pet();        // Whiskers purrs happily!

Types of Inheritance

Different languages support different forms of inheritance:

Single Inheritance:
  Dog โ”€โ”€โ–ถ Animal
  (one parent)

Multilevel Inheritance:
  Puppy โ”€โ”€โ–ถ Dog โ”€โ”€โ–ถ Animal
  (chain of parents)

Hierarchical Inheritance:
  Dog โ”€โ”€โ–ถ Animal
  Cat โ”€โ”€โ–ถ Animal
  (multiple children share one parent)

Multiple Inheritance (not supported in many languages):
  SwimAndFly โ”€โ”€โ–ถ Swimming, Flying
  (one child, multiple parents)

Most OO languages (Java, C#, Python, JavaScript) don't support multiple inheritance of classes to avoid the "diamond problem." Instead, they use interfaces to achieve similar flexibility.

Polymorphism โ€” Many Forms

Polymorphism means "many forms." It allows objects of different types to be treated through the same interface. The same method call can produce different behavior depending on the actual type of the object. This is one of the most powerful features of OO programming.

class PaymentMethod {
  pay(amount) { throw new Error("Implement pay()"); }
  refund(transactionId) { throw new Error("Implement refund()"); }
}

class CreditCard extends PaymentMethod {
  #number;
  constructor(number) { super(); this.#number = number; }

  pay(amount) {
    const last4 = this.#number.slice(-4);
    console.log(`Charged $${amount} to card ending in ${last4}`);
    return { id: "cc_" + Date.now(), method: "credit_card" };
  }

  refund(transactionId) {
    console.log(`Refunded credit card transaction ${transactionId}`);
  }
}

class PayPal extends PaymentMethod {
  #email;
  constructor(email) { super(); this.#email = email; }

  pay(amount) {
    console.log(`Paid $${amount} via PayPal (${this.#email})`);
    return { id: "pp_" + Date.now(), method: "paypal" };
  }

  refund(transactionId) {
    console.log(`Refunded PayPal transaction ${transactionId}`);
  }
}

class CryptoWallet extends PaymentMethod {
  #walletAddress;
  constructor(address) { super(); this.#walletAddress = address; }

  pay(amount) {
    const short = this.#walletAddress.slice(0, 8) + "...";
    console.log(`Sent ${amount} BTC from wallet ${short}`);
    return { id: "crypto_" + Date.now(), method: "crypto" };
  }

  refund(transactionId) {
    console.log(`Crypto refund initiated for ${transactionId}`);
  }
}

// Polymorphism in action โ€” same code, different behavior
function checkout(paymentMethod, cartTotal) {
  console.log(`Processing payment of $${cartTotal}...`);
  const transaction = paymentMethod.pay(cartTotal);
  console.log(`Transaction complete: ${transaction.id}\n`);
  return transaction;
}

// All three work identically through the same interface
const cc = new CreditCard("4111111111111234");
const pp = new PayPal("user@example.com");
const crypto = new CryptoWallet("0x742d35Cc6634C0532925a3b844Bc9e7595f2bD68");

checkout(cc, 99.99);      // Charged $99.99 to card ending in 1234
checkout(pp, 49.99);      // Paid $49.99 via PayPal (user@example.com)
checkout(crypto, 199.99); // Sent 199.99 BTC from wallet 0x742d35...

Polymorphism Types

There are several forms of polymorphism in OO languages:

1. Ad-hoc Polymorphism (Method Overloading)
   Same method name, different parameter lists:
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ area()              โ†’ default area       โ”‚
   โ”‚ area(radius)        โ†’ circle area        โ”‚
   โ”‚ area(width, height) โ†’ rectangle area     โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2. Parametric Polymorphism (Generics)
   Same code works with different types:
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ List<String> strings = new List<>();    โ”‚
   โ”‚ List<Integer> numbers = new List<>();   โ”‚
   โ”‚ // Same List code, different types       โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

3. Subtype Polymorphism (Inheritance)
   Same interface, different implementations:
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ Shape s = new Circle(5);                 โ”‚
   โ”‚ Shape s = new Rectangle(4, 6);           โ”‚
   โ”‚ s.area() โ†’ different behavior            โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Inheritance vs Composition

While inheritance is powerful, it creates tight coupling between parent and child. Modern OO design favors composition over inheritance โ€” building complex objects by combining simpler ones rather than through deep class hierarchies.

// Inheritance โ€” tight coupling, rigid hierarchy
class ElectricCar extends Car {
  charge() { /* ... */ }
}

// Composition โ€” flexible, loosely coupled
class Car {
  constructor(engine, transmission, fuelSystem) {
    this.engine = engine;
    this.transmission = transmission;
    this.fuelSystem = fuelSystem;
  }
}

class ElectricEngine {
  charge() { console.log("Charging battery..."); }
}

class GasEngine {
  refuel() { console.log("Filling gas tank..."); }
}

// Easily swap behaviors
const tesla = new Car(new ElectricEngine(), new AutomaticTransmission(), null);
const ford = new Car(new GasEngine(), new ManualTransmission(), new GasTank());

๐Ÿงช Quick Quiz

In inheritance, what is the subclass also called?