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 kWhOverride 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()
}
}