Kotlin Getters and Setters

In Kotlin getters and setters are automatically generated for properties declared in a class. These accessors provide a way to read (get) and modify (set) the values of properties, ensuring encapsulation and control over data access. Let’s delve into how Kotlin handles getters and setters and explore a real-world example.

Basics of Getters and Setters in Kotlin

In Kotlin, you can define properties directly in a class using the var or val keywords. These properties automatically have getters and setters generated for them, based on their mutability.

  • val properties have a getter but no setter, making them read-only.
  • var properties have both a getter and a setter, allowing them to be read and modified.

Here’s a basic example demonstrating the usage of getters and setters in Kotlin:

class Person {
    var name: String = ""
        get() = field.toUpperCase()  // Custom getter
        set(value) {
            field = value.trim()  // Custom setter
        }
}

In this example, the Person class has a mutable property name with a custom getter and setter. The getter converts the name to uppercase, and the setter trims any leading or trailing whitespace from the provided value before assigning it to the property.

Real-World Example: Employee Management System

Let’s consider a real-world scenario where Kotlin getters and setters are utilized in an Employee Management System. We’ll create a simplified version to showcase their usage.

class Employee {
    var id: Int = 0
        get() = field  // Getter
        set(value) {
            field = value.takeIf { it >= 1000 } ?: throw IllegalArgumentException("Invalid ID")
        }

    var name: String = ""
        get() = field  // Getter
        set(value) {
            field = value.trim().capitalize()  // Setter
        }

    var department: String = ""
        get() = field  // Getter
        set(value) {
            field = value.toUpperCase()  // Setter
        }

    var salary: Double = 0.0
        get() = field  // Getter
        set(value) {
            field = value.takeIf { it >= 0 } ?: throw IllegalArgumentException("Invalid Salary")
        }

    override fun toString(): String {
        return "Employee ID: $id, Name: $name, Department: $department, Salary: $salary"
    }
}

In this example, the Employee class represents an employee with properties such as ID, name, department, and salary. Each property has a custom getter and setter to enforce business rules and data validation.

Example Usage and Output

Now, let’s create and use instances of the Employee class:

fun main() {
    val employee1 = Employee()
    employee1.id = 1001
    employee1.name = "John Doe"
    employee1.department = "IT"
    employee1.salary = 50000.0

    val employee2 = Employee()
    employee2.id = 999  // This will throw an IllegalArgumentException due to invalid ID
}

When we run the above code, the output will be:

Employee ID: 1001, Name: John Doe, Department: IT, Salary: 50000.0
Exception in thread "main" java.lang.IllegalArgumentException: Invalid ID
    at Employee.setId(<filename>:11)
    at EmployeeKt.main(<filename>:6)

The output demonstrates the creation of two employee instances. The first employee is created successfully with valid data, while the second employee’s creation fails due to an invalid ID, enforcing the validation logic defined in the setter.

Kotlin's automatic generation of getters and setters simplifies property management and enhances code readability. By leveraging custom getters and setters, developers can enforce data validation, apply business rules, and encapsulate property access, contributing to more robust and maintainable software.