Skip to main content

Kotlin Inheritance Basics

Introduction

Inheritance is one of the four pillars of Object-Oriented Programming (OOP), alongside encapsulation, polymorphism, and abstraction. In Kotlin, inheritance allows a class to inherit properties and behaviors from another class, promoting code reusability and establishing hierarchical relationships between classes.

In this tutorial, we'll explore the basics of inheritance in Kotlin, including:

  • How to create parent and child classes
  • The open keyword in Kotlin
  • Overriding methods and properties
  • Accessing parent class members using super
  • Constructors in inheritance

Understanding Inheritance in Kotlin

In Kotlin, unlike Java, classes are final by default, which means they cannot be inherited from unless explicitly marked with the open keyword. This design choice was made to promote intentional inheritance rather than accidental inheritance.

Basic Inheritance Syntax

Here's the basic syntax for creating a parent-child relationship between classes:

kotlin
// Parent class (Base class)
open class Parent {
// Properties and methods
}

// Child class (Derived class)
class Child : Parent() {
// Additional properties and methods
}

Let's see a simple example:

kotlin
open class Animal {
var name: String = ""
var age: Int = 0

fun eat() {
println("$name is eating")
}

fun sleep() {
println("$name is sleeping")
}
}

class Dog : Animal() {
fun bark() {
println("$name is barking")
}
}

fun main() {
val myDog = Dog()
myDog.name = "Buddy"
myDog.age = 3

myDog.eat() // Calling method from parent class
myDog.sleep() // Calling method from parent class
myDog.bark() // Calling method from child class
}

Output:

Buddy is eating
Buddy is sleeping
Buddy is barking

In this example, the Dog class inherits properties (name, age) and methods (eat(), sleep()) from the Animal class, while also defining its own method (bark()).

The open Keyword

In Kotlin, you must explicitly mark classes as open to allow inheritance:

kotlin
open class Shape {
// This class can be inherited
}

class Rectangle : Shape() {
// This is OK
}

class Square : Rectangle() {
// Error: Class 'Rectangle' is final and cannot be inherited from
}

The Square class would cause a compilation error because Rectangle is not marked as open.

Overriding Methods and Properties

To allow method overriding, the method in the parent class must be marked as open:

kotlin
open class Animal {
open fun makeSound() {
println("Animal makes a sound")
}

fun sleep() {
println("Animal is sleeping")
}
}

class Cat : Animal() {
override fun makeSound() {
println("Meow!")
}

// This would cause an error because sleep() is not marked as open
// override fun sleep() {
// println("Cat is sleeping")
// }
}

fun main() {
val animal = Animal()
val cat = Cat()

animal.makeSound() // Calls Animal's makeSound()
cat.makeSound() // Calls Cat's makeSound()
}

Output:

Animal makes a sound
Meow!

Final Override

When overriding a method, you can prevent further overriding by using the final keyword:

kotlin
open class Animal {
open fun makeSound() {
println("Animal makes a sound")
}
}

open class Dog : Animal() {
final override fun makeSound() {
println("Woof!")
}
}

class Poodle : Dog() {
// This would cause an error
// override fun makeSound() {
// println("Fancy woof!")
// }
}

Accessing Parent Class Members with super

Use the super keyword to call the parent class's implementation of a method:

kotlin
open class Vehicle {
open fun start() {
println("Vehicle is starting")
}
}

class Car : Vehicle() {
override fun start() {
super.start() // Call parent implementation first
println("Car engine starts")
}
}

fun main() {
val myCar = Car()
myCar.start()
}

Output:

Vehicle is starting
Car engine starts

Constructors in Inheritance

When a class inherits from another class, the primary constructor of the derived class must initialize the base class:

kotlin
open class Person(val name: String, val age: Int) {
fun introduce() {
println("Hi, I'm $name and I'm $age years old")
}
}

// Primary constructor passes parameters to parent constructor
class Student(name: String, age: Int, val studentId: String) : Person(name, age) {
fun study() {
println("$name is studying. Student ID: $studentId")
}
}

fun main() {
val student = Student("Alice", 20, "S12345")
student.introduce()
student.study()
}

Output:

Hi, I'm Alice and I'm 20 years old
Alice is studying. Student ID: S12345

Secondary Constructors

If you're using secondary constructors, you must call the primary constructor or a secondary constructor of the parent class:

kotlin
open class Person(val name: String) {
constructor(name: String, age: Int) : this(name) {
println("Person secondary constructor: $name, $age")
}
}

class Employee : Person {
constructor(name: String) : super(name) {
println("Employee constructor with name only")
}

constructor(name: String, age: Int) : super(name, age) {
println("Employee constructor with name and age")
}
}

fun main() {
val emp1 = Employee("John")
println("---")
val emp2 = Employee("Jane", 30)
}

Output:

Employee constructor with name only
---
Person secondary constructor: Jane, 30
Employee constructor with name and age

Real-World Example: Building a Shape Hierarchy

Here's a more practical example of inheritance for geometric shapes:

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

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

fun describe(): String = "Circle with radius $radius"
}

class Rectangle(private val width: Double, private val height: Double) : Shape() {
override fun area(): Double = width * height
override fun perimeter(): Double = 2 * (width + height)

fun describe(): String = "Rectangle with width $width and height $height"
}

fun main() {
val shapes = arrayOf(
Circle(5.0),
Rectangle(4.0, 6.0)
)

for (shape in shapes) {
println("Area: ${shape.area()}, Perimeter: ${shape.perimeter()}")

// Using is and smart casting to access specific methods
if (shape is Circle) {
println(shape.describe())
} else if (shape is Rectangle) {
println(shape.describe())
}
println("---")
}
}

Output:

Area: 78.53981633974483, Perimeter: 31.41592653589793
Circle with radius 5.0
---
Area: 24.0, Perimeter: 20.0
Rectangle with width 4.0 and height 6.0
---

This example demonstrates polymorphism (another OOP principle) working alongside inheritance.

Multi-level Inheritance

Kotlin supports multi-level inheritance, where a class inherits from another class, which in turn inherits from a third class:

kotlin
open class Animal {
open fun makeSound() {
println("Animal sound")
}
}

open class Mammal : Animal() {
override fun makeSound() {
println("Mammal sound")
}

fun giveBirth() {
println("Giving birth to live young")
}
}

class Cat : Mammal() {
override fun makeSound() {
println("Meow")
}
}

fun main() {
val cat = Cat()
cat.makeSound() // From Cat
cat.giveBirth() // From Mammal
}

Output:

Meow
Giving birth to live young

Summary

In this tutorial, we've covered the basics of inheritance in Kotlin:

  • Classes in Kotlin are final by default and must be marked as open to allow inheritance
  • Methods and properties must also be marked as open to be overridden
  • The override keyword is required when overriding methods or properties
  • The super keyword allows access to parent class members
  • Constructors of the derived class must initialize the base class
  • Inheritance promotes code reuse and establishing hierarchical relationships

Inheritance is a powerful mechanism for building maintainable, extensible code. In Kotlin, the explicit nature of inheritance (requiring open and override keywords) helps you write more intentional code and avoid accidental inheritance issues.

Exercises

  1. Create a Vehicle class hierarchy with Car, Motorcycle, and Bus classes. Add appropriate properties and methods to each class.
  2. Implement a BankAccount hierarchy with base class Account and derived classes SavingsAccount and CheckingAccount.
  3. Create an Employee hierarchy for a company, with different types of employees inheriting from a common base class.
  4. Implement a class hierarchy for a simple game with characters that have different abilities.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)