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());