Getters and Setters in Kotlin Programming Language

Getters and setters are used in object-oriented programming to provide controlled access to class properties.

Introduction

Kotlin is a modern, statically typed programming language designed to be fully interoperable with Java while offering a more concise and expressive syntax. One of its many powerful features includes properties with built-in getters and setters, which make it easier to work with encapsulation and data manipulation.

Getters and setters are used in object-oriented programming to provide controlled access to class properties. In Kotlin, properties have default getter and setter implementations, reducing boilerplate code significantly compared to Java. This blog post will explore getters and setters in Kotlin, their benefits, customization options, and best practices.


What Are Getters and Setters?

In traditional object-oriented programming languages like Java, getters and setters are explicitly defined methods used to access and modify private properties. For example, in Java, we might define a property with a getter and setter like this:

public class Person {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Kotlin simplifies this with properties that automatically generate getter and setter methods when needed.

class Person {
    var name: String = ""
}

The name property in Kotlin already has an implicit getter and setter. The compiler generates equivalent methods under the hood, so we don’t have to write them explicitly unless customization is required.


Understanding Default Getters and Setters in Kotlin

Every property declared in Kotlin has a default getter, and mutable (var) properties also have a default setter. Here’s how they work:

  • Getter (get()): Retrieves the value of a property.
  • Setter (set(value)): Updates the value of a mutable property.

Example:

class Car {
    var model: String = "Tesla"
}

fun main() {
    val car = Car()
    println(car.model) // Calls the default getter
    car.model = "Ford" // Calls the default setter
    println(car.model) // Outputs: Ford
}

Here, model is a property with an implicit getter and setter. Since model is declared as var, we can update its value.

For read-only (val) properties, only a getter is generated by default:

class Book {
    val title: String = "Kotlin for Beginners"
}

fun main() {
    val book = Book()
    println(book.title) // Calls the default getter
    // book.title = "Advanced Kotlin" // Error: Val cannot be reassigned
}

Since title is declared with val, it is immutable and does not have a setter.


Custom Getters and Setters in Kotlin

Kotlin allows us to customize property getters and setters based on specific requirements.

Custom Getter

A custom getter can be used to modify or format the value before returning it.

class Employee {
    var salary: Double = 5000.0
    
    val bonus: Double
        get() = salary * 0.1 // Custom getter calculating bonus
}

fun main() {
    val employee = Employee()
    println(employee.bonus) // Outputs: 500.0
}

Here, bonus is computed dynamically using a custom getter.

Custom Setter

A custom setter allows us to control how values are assigned to a property.

class Student {
    var grade: Int = 0
        set(value) {
            field = if (value in 0..100) value else throw IllegalArgumentException("Invalid grade")
        }
}

fun main() {
    val student = Student()
    student.grade = 85 // Works fine
    println(student.grade) // Outputs: 85
    
    // student.grade = 120 // Throws exception: Invalid grade
}

Here, we ensure that grade is always within the range of 0 to 100 by validating it in the setter.


Backing Fields in Kotlin

One crucial aspect of custom setters is the use of backing fields. The field keyword is a special identifier that refers to the backing field of a property, preventing infinite recursion.

For example:

class Person {
    var age: Int = 18
        set(value) {
            field = if (value > 0) value else throw IllegalArgumentException("Age must be positive")
        }
}

Here, field ensures that the assignment field = value happens without calling the setter recursively.


Computed Properties vs. Backing Properties

Computed Properties

A computed property does not store a value; instead, it computes the value dynamically each time it is accessed.

class Rectangle(val width: Int, val height: Int) {
    val area: Int
        get() = width * height
}

fun main() {
    val rect = Rectangle(5, 10)
    println(rect.area) // Outputs: 50
}

Backing Properties

A backing property is used when we want to store a value but expose only a computed or controlled version of it.

class Person {
    private var _nickname: String = "Unknown"
    
    var nickname: String
        get() = _nickname
        set(value) {
            _nickname = value.capitalize()
        }
}

fun main() {
    val person = Person()
    person.nickname = "john"
    println(person.nickname) // Outputs: John
}

Here, _nickname acts as a backing property, allowing us to control how nickname is modified and accessed.


Best Practices for Using Getters and Setters in Kotlin

  1. Prefer Properties Over Methods: Instead of writing explicit getter and setter methods like in Java, use Kotlin properties.
  2. Use Custom Getters for Computed Properties: If a property’s value depends on other properties, consider using a custom getter.
  3. Validate Data in Setters: Custom setters help enforce constraints and prevent invalid assignments.
  4. Use Backing Fields When Necessary: Always use field inside a setter to avoid infinite recursion.
  5. Readability Matters: Keep your property definitions clean and concise for better readability.

Conclusion

Kotlin simplifies working with getters and setters by providing default implementations while allowing customization when needed. By leveraging custom getters and setters, computed properties, and backing properties, developers can write cleaner, more maintainable code. Understanding these concepts is crucial for building robust Kotlin applications.

By following best practices and leveraging Kotlin’s property features effectively, developers can significantly enhance code readability and maintainability.


Last modified 20.02.2025: new kotlin and mint content (93a1000)