devnotes.

Understanding Abstract Classes

An abstract class is a class that cannot be instantiated directly — you can never write new Shape() if Shape is abstract. It exists purely to be extended, providing a partial implementation that concrete subclasses complete.

abstract class Employee {
    protected name
    protected baseSalary

    function __construct(name, baseSalary) {
        this.name = name
        this.baseSalary = baseSalary
    }

    // Concrete method — shared, fully implemented, inherited as-is
    function getName(): string {
        return this.name
    }

    // Abstract method — no body; each subclass MUST provide its own
    abstract function calculateBonus(): number

    function getTotalPay(): number {
        return this.baseSalary + this.calculateBonus()
    }
}

class SalesEmployee extends Employee {
    function calculateBonus(): number {
        return this.baseSalary * 0.1 // 10% commission-style bonus
    }
}

class EngineerEmployee extends Employee {
    function calculateBonus(): number {
        return 500 // flat annual bonus
    }
}

// employee = new Employee("X", 1000) // ERROR — cannot instantiate an abstract class
sales = new SalesEmployee("Alice", 3000)
print(sales.getTotalPay()) // uses the shared getTotalPay(), calling SalesEmployee's own calculateBonus()

Why abstract classes are useful: they let you share real, working code (getName(), getTotalPay()) across a family of related classes, while still forcing each subclass to supply the pieces that genuinely differ (calculateBonus()). This is different from a plain interface, which cannot share any implementation at all (in the traditional sense), and different from a regular parent class, which doesn't force subclasses to override anything.

An abstract class with zero abstract methods is legal in most languages but somewhat unusual — the main point of an abstract class is usually to combine shared implementation with at least one required, subclass-specific method.

Understanding Abstract Classes in OOP | DevNotes