Kotlin Tutoial

Basics

Control Flow

Array & String

Functions

Collections

OOPs Concept

Exception Handling

Null Safety

Regex & Ranges

Java Interoperability

Miscellaneous

Android

Kotlin | Class Properties and Custom Accessors

In Kotlin, classes can have properties. A property can be mutable (using var) or immutable (using val). Kotlin provides a concise syntax for declaring properties and offers powerful features like custom accessors (getters and setters) to control how properties are read or modified.

1. Declaring Properties:

You can declare properties directly in the primary constructor or inside the class body:

class Person(val name: String, var age: Int)

In this Person class, name is a read-only property, and age is a mutable property.

2. Default Accessors:

By default, Kotlin provides getter methods for all properties and setter methods for var properties:

val person = Person("Alice", 25)
println(person.name)  // Calls the default getter for name
person.age = 26       // Calls the default setter for age

3. Custom Accessors:

You can define custom accessors (getter and setter) for properties to add custom logic when getting or setting property values.

Custom Getter:

class Rectangle(val width: Int, val height: Int) {
    val isSquare: Boolean
        get() {
            return width == height
        }
}

val rect = Rectangle(10, 20)
println(rect.isSquare)  // Outputs: false

In the example, the isSquare property doesn't store any value. It calculates its value every time you access it.

Custom Setter:

For mutable properties (var), you can provide a custom setter:

class Person(val name: String, age: Int) {
    var age = age
        set(value) {
            if (value > 0) {
                field = value
            } else {
                println("Age can't be negative!")
            }
        }
}

val person = Person("Bob", 30)
person.age = -5  // Outputs: Age can't be negative!

In the setter, field is a special keyword that refers to the backing field of the property. You use it to update the actual stored value.

4. Backing Fields:

Kotlin generates a private backing field for properties that need it. This field is accessible using the field keyword inside the accessors. If you don't use custom accessors that reference the backing field, Kotlin won't create one for the property (like in the case of isSquare in the above example).

5. Late-Initialized Properties:

Sometimes you can't initialize a non-null property in the constructor. For such cases, Kotlin provides lateinit:

class Database {
    lateinit var connection: Connection

    fun connect() {
        connection = Connection()  // Initialize the property
    }
}

class Connection

With lateinit, you promise the compiler that you'll initialize the property before accessing it, so it doesn't have to be nullable or immediately initialized.

Conclusion:

Kotlin's property system with custom accessors provides a robust mechanism to ensure encapsulation and expressiveness. It allows developers to create clear and safe interfaces while hiding the internal logic and validation associated with property access.

  1. Custom getters and setters in Kotlin: You can define custom getters and setters for properties.

    class Person {
        var age: Int = 0
            get() = field + 1
            set(value) {
                field = if (value > 0) value else 0
            }
    }
    
  2. Working with properties in Kotlin classes: Properties in Kotlin provide a concise syntax for defining fields with custom behavior.

    class Circle(var radius: Double) {
        val diameter: Double
            get() = 2 * radius
    }
    
  3. Delegated properties in Kotlin: Kotlin supports delegated properties, allowing you to delegate the implementation of the property to another class.

    class Example {
        var delegatedValue: String by DelegateClass()
    }
    
  4. Property initialization in Kotlin: Properties can be initialized when declared or in the constructor.

    class Car(make: String, model: String) {
        var make: String = make
        var model: String = model
    }
    
  5. Late-initialized properties in Kotlin: Properties can be marked as lateinit if they are initialized later (after the object is created).

    class Example {
        lateinit var lateInitializedProperty: String
    }
    
  6. Backing fields in Kotlin properties: Kotlin automatically generates backing fields for properties. You can reference this field using the field keyword.

    var counter: Int = 0
        set(value) {
            if (value > field) {
                field = value
            }
        }
    
  7. Observable properties in Kotlin: You can use the observable delegate to observe changes to a property.

    import kotlin.properties.Delegates
    
    class Example {
        var observableProperty: String by Delegates.observable("Initial Value") { _, old, new ->
            println("Property changed from $old to $new")
        }
    }
    
  8. Computed properties in Kotlin: Properties can be computed on the fly using custom getter logic.

    class Circle(var radius: Double) {
        val area: Double
            get() = 3.14 * radius * radius
    }
    
  9. Custom accessor functions in Kotlin: You can define custom accessor functions for properties to encapsulate behavior.

    class Temperature(var celsius: Double) {
        val fahrenheit: Double
            get() = celsius * 9 / 5 + 32
    }
    
  10. Property visibility modifiers in Kotlin: Kotlin allows you to control the visibility of properties using modifiers like private, protected, and internal.

    class Example {
        private var privateProperty: String = "Private"
    }
    
  11. Property extension functions in Kotlin: You can define extension functions for properties to add functionality.

    val String.isEvenLength: Boolean
        get() = length % 2 == 0
    
  12. Immutable properties in Kotlin: Properties declared with val are immutable. Once assigned, their values cannot be changed.

    class Example {
        val constantProperty: Int = 42
    }