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:
// 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:
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:
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
:
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:
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:
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:
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:
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:
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:
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
- Create a
Vehicle
class hierarchy withCar
,Motorcycle
, andBus
classes. Add appropriate properties and methods to each class. - Implement a
BankAccount
hierarchy with base classAccount
and derived classesSavingsAccount
andCheckingAccount
. - Create an
Employee
hierarchy for a company, with different types of employees inheriting from a common base class. - 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! :)