Kotlin – 11 – Object-Oriented Programming in Kotlin

Object-oriented programming (OOP) is a fundamental paradigm in software development, and Kotlin is designed with strong support for OOP principles. In this guide, we’ll explore object-oriented programming in Kotlin, including classes, objects, inheritance, encapsulation, and polymorphism, along with examples to illustrate these concepts.

Classes and Objects

In Kotlin, classes serve as blueprints for creating objects. Classes define the structure and behavior of objects. You can create instances (objects) of a class to work with its properties and methods. Here’s how to define a simple class:

class Person {
    var name: String = ""
    var age: Int = 0
}

In this example, we’ve defined a Person class with two properties: name and age. To create an instance of this class, you can use the Person constructor:

val person = Person()
person.name = "Alice"
person.age = 30

Now, the person object has a name of “Alice” and an age of 30.

Constructors

Kotlin classes can have one or more constructors. The primary constructor is defined within the class header, while secondary constructors are declared with the constructor keyword. Here’s an example of a class with a primary constructor and a secondary constructor:

class Book(val title: String, val author: String) {
    var pages: Int = 0

    constructor(title: String, author: String, pages: Int) : this(title, author) {
        this.pages = pages
    }
}

In this example, the primary constructor is defined in the class header, taking two parameters. The secondary constructor is created using the constructor keyword and takes an additional pages parameter. It initializes the properties using the this keyword and delegates to the primary constructor.

Inheritance

Kotlin supports single inheritance, meaning a class can inherit from only one superclass. To declare inheritance, you use the : symbol followed by the name of the superclass. Here’s an example of a class hierarchy:

open class Animal(val name: String)

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

In this example, we have a superclass Animal with a name property. The Dog class inherits from Animal using the : symbol and defines its own method bark.

Encapsulation

Kotlin supports encapsulation by providing access modifiers to control the visibility of properties and methods within a class. The most common access modifiers are public, private, protected, and internal. By default, properties and methods are public. Here’s an example demonstrating encapsulation:

class BankAccount(private val accountNumber: String, var balance: Double) {
    fun deposit(amount: Double) {
        balance += amount
    }

    fun withdraw(amount: Double) {
        if (amount <= balance) {
            balance -= amount
        } else {
            println("Insufficient funds")
        }
    }

    fun getAccountInfo() {
        println("Account Number: $accountNumber, Balance: $balance")
    }
}

In this example, the accountNumber property is private, meaning it can only be accessed within the BankAccount class. The balance property is public and can be accessed and modified from outside the class. The methods deposit, withdraw, and getAccountInfo provide controlled access to the properties.

Polymorphism

Kotlin supports polymorphism, allowing you to define functions that can work with objects of different classes in a unified way. This is achieved through inheritance and interface implementations. Here’s an example using polymorphism with a Shape hierarchy:

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

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

class Rectangle(val width: Double, val height: Double) : Shape() {
    override fun area(): Double {
        return width * height
    }
}

In this example, the Shape class defines a virtual method area(). The Circle and Rectangle classes inherit from Shape and override the area() method to calculate the area specific to each shape.

Command and Example

Here’s an example demonstrating the use of classes and inheritance in Kotlin:

open class Vehicle(val brand: String) {
    open fun drive() {
        println("$brand is driving")
    }
}

class Car(brand: String, val model: String) : Vehicle(brand) {
    override fun drive() {
        println("$brand $model is driving")
    }
}

fun main() {
    val car = Car("Toyota", "Camry")
    car.drive()
}

In this example, we have a Vehicle superclass with a drive() method and a Car subclass that overrides the drive() method. The main() function demonstrates creating a Car object and invoking the drive() method.

Object-oriented programming is a key concept in Kotlin, providing the means to structure your code using classes, inheritance, encapsulation, and polymorphism. These principles help you build modular, maintainable, and extensible software solutions. Understanding how to design and work with classes and objects is essential for effective Kotlin development.