Kotlin Inheritance
Inheritance lets one class reuse the properties and methods of another class. The class that gives its features is the parent class (also called superclass or base class). The class that receives those features is the child class (also called subclass or derived class). This prevents code duplication — write once, share everywhere.
The Problem Inheritance Solves
Without inheritance:
class Dog {
var name: String = ""
fun breathe() { println("Breathing") }
fun bark() { println("Woof!") }
}
class Cat {
var name: String = ""
fun breathe() { println("Breathing") } // duplicate!
fun meow() { println("Meow!") }
}
With inheritance:
open class Animal {
var name: String = ""
fun breathe() { println("Breathing") }
}
class Dog : Animal() {
fun bark() { println("Woof!") }
}
class Cat : Animal() {
fun meow() { println("Meow!") }
}
The open keyword is required on any class that can be inherited. By default, Kotlin classes are closed — they cannot be extended.
Diagram — Inheritance Hierarchy
Animal (parent)
name: String
breathe()
/ \
/ \
Dog (child) Cat (child)
bark() meow()
Dog inherits: name, breathe()
Cat inherits: name, breathe()
Calling the Parent Constructor
open class Vehicle(val brand: String, val year: Int) {
fun describe() {
println("$brand ($year)")
}
}
class ElectricCar(brand: String, year: Int, val range: Int) : Vehicle(brand, year) {
fun showRange() {
println("Range: $range km")
}
}
val tesla = ElectricCar("Tesla", 2024, 500)
tesla.describe() // Tesla (2024)
tesla.showRange() // Range: 500 km
Overriding Methods
A child class can replace a parent's method with its own version. Mark the parent method with open and the child method with override.
open class Shape {
open fun area(): Double = 0.0
open fun describe() {
println("I am a shape with area ${area()}")
}
}
class Circle(val radius: Double) : Shape() {
override fun area(): Double = 3.14159 * radius * radius
}
class Rectangle(val width: Double, val height: Double) : Shape() {
override fun area(): Double = width * height
}
val c = Circle(5.0)
c.describe() // I am a shape with area 78.53975
val r = Rectangle(4.0, 6.0)
r.describe() // I am a shape with area 24.0
Calling the Parent Implementation with super
Use super to call the parent class version of a method from inside the child class.
open class Notification {
open fun send() {
println("Sending generic notification...")
}
}
class PushNotification : Notification() {
override fun send() {
super.send() // runs parent's send() first
println("Also sending push to device.")
}
}
PushNotification().send()
// Sending generic notification...
// Also sending push to device.
Overriding Properties
open class Device {
open val maxVolume: Int = 10
}
class Headphones : Device() {
override val maxVolume: Int = 20
}
println(Headphones().maxVolume) // 20
Preventing Further Override with final
open class Animal {
open fun sound() = println("...")
}
class Dog : Animal() {
final override fun sound() = println("Woof!")
// No further subclass can override sound()
}
Abstract Classes
An abstract class cannot be instantiated directly — you cannot create an object from it. It exists to be extended. Abstract methods have no body; subclasses must provide the implementation.
abstract class Employee(val name: String) {
abstract fun calculateSalary(): Double // no body
fun introduce() {
println("I am $name, salary: ${calculateSalary()}")
}
}
class FullTimeEmployee(name: String, val monthlySalary: Double)
: Employee(name) {
override fun calculateSalary() = monthlySalary
}
class Contractor(name: String, val hourlyRate: Double, val hours: Int)
: Employee(name) {
override fun calculateSalary() = hourlyRate * hours
}
FullTimeEmployee("Priya", 50000.0).introduce()
// I am Priya, salary: 50000.0
Contractor("Dev", 500.0, 80).introduce()
// I am Dev, salary: 40000.0
Diagram — Abstract vs Concrete
[Abstract] Employee
name: String
calculateSalary(): Double ← no body, MUST be overridden
introduce() ← has body, inherited as-is
┌────────────────────┐
▼ ▼
FullTimeEmployee Contractor
calculateSalary() calculateSalary()
= monthlySalary = hourlyRate * hours
Any — The Root of All Classes
Every Kotlin class automatically extends the built-in Any class. Any provides three methods available on every object:
val s = "Kotlin"
println(s.equals("Kotlin")) // true
println(s.hashCode()) // some integer
println(s.toString()) // Kotlin
