Skip to content

Inheritance

Inheritance#

Inheritance allows a class (subclass) to acquire properties and methods from another class (superclass). This enables code reuse and establishes "is-a" relationships between classes.

For example, an ElectricGuitar class can inherit from a Guitar class, meaning every electric guitar "is-a" guitar with all guitar characteristics plus electric-specific features.

Class Hierarchy#

The inheritance relationship creates a hierarchical structure where specialized classes extend the base class:

graph TD
    A["Guitar<br/>(Base Class)"] --> B["ElectricGuitar"]
    A --> C["AcousticGuitar"]
    A --> D["ClassicalGuitar"]

    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#fff3e0

Shared Properties and Methods#

The base class defines common attributes and behaviors that all subclasses inherit:

graph LR
    A["Guitar Base Class"] --> B["brand: String"]
    A --> C["model: String"]
    A --> D["play() method"]

    style A fill:#e3f2fd
    style B fill:#c8e6c9
    style C fill:#c8e6c9
    style D fill:#c8e6c9

Specialized Features#

Each subclass adds its own unique properties and methods:

graph TD
    A["ElectricGuitar"] --> B["pickupType: String"]
    A --> C["distort() method"]

    D["AcousticGuitar"] --> E["bodyShape: String"]
    D --> F["fingerpick() method"]

    G["ClassicalGuitar"] --> H["stringType: String"]
    G --> I["pluck() method"]

    style A fill:#fff3e0
    style D fill:#fff3e0
    style G fill:#fff3e0

Benefits of Inheritance:

  • Code Reuse: Share common functionality across related classes
  • Consistency: All subclasses behave predictably
  • Maintainability: Changes to common behavior only need to be made in one place

Let's see how different programming languages implement inheritance:

Java uses the extends keyword for inheritance:

public class Guitar {
    private String brand;
    private String model;

    public Guitar(String brand, String model) {
        this.brand = brand;
        this.model = model;
    }

    public void play() {
        System.out.println("Playing the guitar!");
    }

    @Override
    public String toString() {
        return brand + " " + model;
    }
}

public class ElectricGuitar extends Guitar {
    private String pickupType;

    public ElectricGuitar(String brand, String model, String pickupType) {
        super(brand, model); // Calls the constructor of the superclass Guitar
        this.pickupType = pickupType;
    }

    public void distort() {
        System.out.println("Applying distortion!");
    }
}

// Create an instance of ElectricGuitar
ElectricGuitar electricGuitar = new ElectricGuitar("Fender", "Stratocaster", "Single Coil");
electricGuitar.play();  // Output: Playing the guitar!
electricGuitar.distort();  // Output: Applying distortion!
System.out.println(electricGuitar);  // Output: Fender Stratocaster

Kotlin uses the : symbol for inheritance:

open class Guitar(val brand: String, val model: String) {
    open fun play() {
        println("Playing the guitar!")
    }

    override fun toString(): String {
        return "$brand $model";
    }
}

class ElectricGuitar(brand: String, model: String, val pickupType: String) : Guitar(brand, model) {
    fun distort() {
        println("Applying distortion!");
    }
}

// Create an instance of ElectricGuitar
val electricGuitar = ElectricGuitar("Fender", "Stratocaster", "Single Coil");
electricGuitar.play();  // Output: Playing the guitar!
electricGuitar.distort();  // Output: Applying distortion!
println(electricGuitar);  // Output: Fender Stratocaster

TypeScript uses the extends keyword for inheritance:

class Guitar {
    constructor(public brand: string, public model: string) {}

    play() {
        console.log("Playing the guitar!");
    }

    toString(): string {
        return `${this.brand} ${this.model}`;
    }
}

class ElectricGuitar extends Guitar {
    constructor(brand: string, model: string, public pickupType: string) {
        super(brand, model); // Calls the constructor of the superclass Guitar
    }

    distort() {
        console.log("Applying distortion!");
    }
}

// Create an instance of ElectricGuitar
const electricGuitar = new ElectricGuitar("Fender", "Stratocaster", "Single Coil");
electricGuitar.play();  // Output: Playing the guitar!
electricGuitar.distort();  // Output: Applying distortion!
console.log(electricGuitar.toString());  // Output: Fender Stratocaster

Dart uses the extends keyword for inheritance:

class Guitar {
    String brand;
    String model;

    Guitar(this.brand, this.model);

    void play() {
        print("Playing the guitar!");
    }

    @override
    String toString() {
        return "$brand $model";
    }
}

class ElectricGuitar extends Guitar {
    String pickupType;

    ElectricGuitar(String brand, String model, this.pickupType) : super(brand, model); // Calls the constructor of the superclass Guitar

    void distort() {
        print("Applying distortion!");
    }
}

// Create an instance of ElectricGuitar
var electricGuitar = ElectricGuitar("Fender", "Stratocaster", "Single Coil");
electricGuitar.play();  // Output: Playing the guitar!
electricGuitar.distort();  // Output: Applying distortion!
print(electricGuitar);  // Output: Fender Stratocaster

Swift uses the : symbol for inheritance:

class Guitar {
    var brand: String
    var model: String

    init(brand: String, model: String) {
        self.brand = brand
        self.model = model
    }

    func play() {
        print("Playing the guitar!")
    }

    var description: String {
        return "\(brand) \(model)"
    }
}

class ElectricGuitar: Guitar {
    var pickupType: String

    init(brand: String, model: String, pickupType: String) {
        self.pickupType = pickupType;
        super.init(brand: brand, model: model); // Calls the constructor of the superclass Guitar
    }

    func distort() {
        print("Applying distortion!");
    }
}

// Create an instance of ElectricGuitar
let electricGuitar = ElectricGuitar(brand: "Fender", model: "Stratocaster", pickupType: "Single Coil");
electricGuitar.play();  // Output: Playing the guitar!
electricGuitar.distort();  // Output: Applying distortion!
print(electricGuitar.description);  // Output: Fender Stratocaster

Python uses parentheses for inheritance:

class Guitar:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

    def play(self):
        print("Playing the guitar!")

    def __str__(self):
        return f"{self.brand} {self.model}"

class ElectricGuitar(Guitar):
    def __init__(self, brand, model, pickup_type):
        super().__init__(brand, model)  # Calls the constructor of the superclass Guitar
        self.pickup_type = pickup_type

    def distort(self):
        print("Applying distortion!")

# Create an instance of ElectricGuitar
electric_guitar = ElectricGuitar("Fender", "Stratocaster", "Single Coil");
electric_guitar.play();  # Output: Playing the guitar!
electric_guitar.distort();  # Output: Applying distortion!
print(electric_guitar);  # Output: Fender Stratocaster