Kotlin Encapsulation

Introduction

Encapsulation is a fundamental concept in object-oriented programming (OOP) that restricts direct access to an object’s internal state and allows modification only through well-defined methods. This helps to protect the integrity of the object’s data and makes the code more modular and maintainable. In Kotlin, encapsulation is achieved using classes, visibility modifiers, and properties with custom getters and setters.

Encapsulation in Kotlin

Encapsulation in Kotlin involves:

  1. Defining a class with private properties.
  2. Providing public methods to access and modify the properties.
  3. Using custom getters and setters to control how properties are accessed and modified.

Example

fun main() {
    val person = Person("Rahul", 25)
    println(person.name)  // Accessing the property directly
    println(person.age)   // Accessing the property through a getter

    person.age = 30       // Modifying the property through a setter
    println(person.age)
}

class Person(val name: String, private var _age: Int) {
    // Custom getter for age
    val age: Int
        get() = _age

    // Custom setter for age
    set(value) {
        if (value > 0) {
            _age = value
        } else {
            println("Invalid age")
        }
    }
}

Explanation:

  • private var _age: Int: The actual age property is private and can’t be accessed directly from outside the class.
  • val age: Int get() = _age: A custom getter that returns the value of _age.
  • var age: Int set(value) { ... }: A custom setter that updates the value of _age if the provided value is valid.

Output:

Rahul
25
30

Benefits of Encapsulation

  • Data Hiding: Internal state of an object is hidden from the outside, preventing unauthorized access and modification.
  • Modularity: Changes to the internal implementation of a class do not affect the code that uses the class.
  • Maintainability: Well-defined interfaces make the code easier to understand, maintain, and extend.

Example with Encapsulation

Let’s consider a more complex example with a BankAccount class.

Example

fun main() {
    val account = BankAccount("123456", 1000.0)
    account.deposit(500.0)
    println("Balance after deposit: ${account.balance}")

    account.withdraw(200.0)
    println("Balance after withdrawal: ${account.balance}")

    account.withdraw(2000.0) // Attempt to withdraw more than the balance
}

class BankAccount(private val accountNumber: String, private var _balance: Double) {
    // Custom getter for balance
    val balance: Double
        get() = _balance

    // Method to deposit money
    fun deposit(amount: Double) {
        if (amount > 0) {
            _balance += amount
        } else {
            println("Invalid deposit amount")
        }
    }

    // Method to withdraw money
    fun withdraw(amount: Double) {
        if (amount > 0 && amount <= _balance) {
            _balance -= amount
        } else {
            println("Invalid withdrawal amount or insufficient funds")
        }
    }
}

Explanation:

  • private var _balance: Double: The actual balance property is private and can’t be accessed directly from outside the class.
  • val balance: Double get() = _balance: A custom getter that returns the current balance.
  • fun deposit(amount: Double) { ... }: A method to deposit money into the account.
  • fun withdraw(amount: Double) { ... }: A method to withdraw money from the account, with checks to prevent invalid operations.

Output:

Balance after deposit: 1500.0
Balance after withdrawal: 1300.0
Invalid withdrawal amount or insufficient funds

Using Backing Fields for Encapsulation

Backing fields allow you to create properties with custom getters and setters while still being able to store the value of the property. The field identifier in Kotlin is used for this purpose.

Example

fun main() {
    val student = Student("Amit")
    student.grade = 85
    println("${student.name}'s grade: ${student.grade}")

    student.grade = 120 // Invalid grade
}

class Student(val name: String) {
    var grade: Int = 0
        set(value) {
            if (value in 0..100) {
                field = value
            } else {
                println("Invalid grade")
            }
        }
}

Explanation:

  • var grade: Int = 0 set(value) { ... }: The property grade uses a backing field to store its value and includes validation in the setter.
  • field = value: The field identifier is used to set the value of the property.

Output:

Amit's grade: 85
Invalid grade

Conclusion

In this chapter, you learned about encapsulation in Kotlin, including the use of visibility modifiers, custom getters and setters, and backing fields. Encapsulation helps to protect the integrity of an object’s data, makes the code more modular, and improves maintainability. Understanding and applying encapsulation is essential for writing robust and maintainable Kotlin programs.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top