Kotlin Inheritance

Inheritance lets one class reuse the properties and functions of another class. The class being inherited from is called the parent (or superclass). The class that inherits is called the child (or subclass). The child gets everything the parent has, and can add or customize behavior.

Open Classes

By default, Kotlin classes are closed — you cannot inherit from them. Add the open keyword to allow inheritance:

open class Animal(val name: String) {
    open fun speak() {
        println("$name makes a sound")
    }

    fun breathe() {
        println("$name breathes")
    }
}

class Dog(name: String) : Animal(name) {
    override fun speak() {
        println("$name barks: Woof!")
    }
}

class Cat(name: String) : Animal(name) {
    override fun speak() {
        println("$name meows: Meow!")
    }
}

fun main() {
    val dog = Dog("Rex")
    val cat = Cat("Whiskers")

    dog.speak()     // Rex barks: Woof!
    cat.speak()     // Whiskers meows: Meow!
    dog.breathe()   // Rex breathes  (inherited, not overridden)
}

Inheritance Diagram


        Animal
       ┌──────────────────────────┐
       │ name: String             │
       │ speak()   ← open         │
       │ breathe() ← not open     │
       └──────────────────────────┘
              ▲               ▲
              │               │
           Dog              Cat
       ┌──────────┐     ┌──────────┐
       │ speak()  │     │ speak()  │
       │ (override│     │ (override│
       │  → Woof) │     │  → Meow) │
       └──────────┘     └──────────┘

Both Dog and Cat inherit:
  - name property
  - breathe() function
Each overrides speak() differently

Calling the Parent with super

open class Vehicle(val brand: String) {
    open fun describe() {
        println("Vehicle: $brand")
    }
}

class ElectricCar(brand: String, val batteryKWh: Int) : Vehicle(brand) {
    override fun describe() {
        super.describe()  // call parent's describe first
        println("Battery: $batteryKWh kWh")
    }
}

fun main() {
    val tesla = ElectricCar("Tesla", 100)
    tesla.describe()
}

Output:

Vehicle: Tesla
Battery: 100 kWh

Override Rules


open class Base {
    open fun canOverride() { }      // can be overridden
    fun cannotOverride() { }        // cannot be overridden (not open)
    open val value: Int = 0         // open property
}

class Child : Base() {
    override fun canOverride() { }  // OK
    // override fun cannotOverride() { }  ← ERROR
    override val value: Int = 42    // OK
}

Preventing Further Override

open class Shape {
    open fun area(): Double = 0.0
}

open class Square(val side: Double) : Shape() {
    final override fun area(): Double = side * side  // final stops further overriding
}

class SpecialSquare(side: Double) : Square(side) {
    // override fun area() = ...   ← ERROR: area() is final in Square
}

Practical Example: Payment System

open class Payment(val amount: Double) {
    open fun process() {
        println("Processing payment of ₹$amount")
    }
    fun receipt() = "Receipt: ₹$amount paid"
}

class CashPayment(amount: Double) : Payment(amount) {
    override fun process() {
        super.process()
        println("Cash collected at counter.")
    }
}

class UpiPayment(amount: Double, val upiId: String) : Payment(amount) {
    override fun process() {
        super.process()
        println("UPI request sent to $upiId")
    }
}

fun main() {
    val payments = listOf(
        CashPayment(500.0),
        UpiPayment(1200.0, "raj@upi"),
        CashPayment(300.0)
    )

    for (p in payments) {
        p.process()
        println(p.receipt())
        println()
    }
}

Leave a Comment

Your email address will not be published. Required fields are marked *