Kotlin Inheritance

Inheritance lets a class reuse and extend the properties and functions of another class.

Opening a Class for Inheritance

By default, every Kotlin class is final, meaning no other class can inherit from it. To allow inheritance, mark the class (and any function you want to be overridable) with the open keyword. This is a deliberate design choice in Kotlin: inheritance must be opted into explicitly.

An Open Base Class

open class Animal(val name: String) {
    open fun makeSound(): String {
        return "..."
    }
}

class Dog(name: String) : Animal(name) {
    override fun makeSound(): String {
        return "Woof!"
    }
}

fun main() {
    val dog = Dog("Rex")
    println("${dog.name} says ${dog.makeSound()}")
}

The Colon and Override Keywords

A subclass declares its parent using a colon followed by the parent class name and its constructor call, like : Animal(name). To replace a parent's open function with new behavior, the subclass uses the override keyword - both open and override are required, so accidental overriding never happens by mistake.

  • open marks a class or member as inheritable/overridable
  • : SuperClass(args) establishes the parent-child relationship
  • override replaces a parent's open function or property in the subclass
  • super.functionName() calls the parent's original implementation from within an override

Calling the Parent With super

open class Vehicle(val brand: String) {
    open fun describe(): String = "A vehicle made by $brand"
}

class Motorcycle(brand: String, val cc: Int) : Vehicle(brand) {
    override fun describe(): String {
        return super.describe() + " with a $cc cc engine"
    }
}

fun main() {
    val moto = Motorcycle("Vantis", 650)
    println(moto.describe())
}
Note: super.describe() lets a subclass build on the parent's behavior instead of fully replacing it, which keeps shared logic in one place.

Polymorphism Through Inheritance

Because a subclass 'is a' kind of its superclass, you can store subclass objects in a variable typed as the superclass. When you call an overridden function on that variable, Kotlin runs the subclass's version at runtime - this is polymorphism, and it's one of the most powerful benefits of inheritance.

Polymorphic Behavior

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

class Circle(val radius: Double) : Shape() {
    override fun area(): Double = Math.PI * radius * radius
}

class Square(val side: Double) : Shape() {
    override fun area(): Double = side * side
}

fun main() {
    val shapes: List<Shape> = listOf(Circle(2.0), Square(3.0))
    for (shape in shapes) {
        println("Area: ${shape.area()}")
    }
}
KeywordPlaced OnEffect
openclass or memberAllows the class/member to be inherited or overridden
: SuperClass()subclass headerDeclares inheritance and calls the parent constructor
overridesubclass memberReplaces the parent's open implementation
super.member()inside an overrideInvokes the parent's original implementation
Note: Trying to inherit from a class that isn't marked open results in a compile-time error: 'This type is final, so it cannot be inherited from'.

With inheritance, you've now covered the four essential OOP building blocks in Kotlin: classes and objects, constructors, class functions, and inheritance with polymorphism.

Exercise: Kotlin Inheritance

Can any Kotlin class be subclassed by default, without changing anything about it?