Mohamad Abuzaid 1 year ago
mohamad-abuzaid #kotlin-tips

Kotlin Delegated Properties

What are delegated properties? What are their benefits? How do we use them?

Delegated Properties


In Kotlin, delegated properties are a mechanism that allows you to delegate the implementation of a property to another object. This can help reduce boilerplate code and simplify the implementation of certain types of properties.

To use delegated properties, you define a property and then specify a delegate using the by keyword. The delegate object must provide a method for getting and setting the property value. There are several built-in delegate types in Kotlin, including:

  1. lazy: This delegate is used for properties that are expensive to compute and should only be computed when they are first accessed. The lazy delegate initializes the property lazily, meaning that the initial value is only calculated when the property is first accessed.
  2. observable: This delegate is used to add a listener to the property so that when the property value is changed, a callback function is called. The observable delegate takes a lambda function that is called when the property value changes.
  3. vetoable: This delegate is similar to observable, but it allows you to veto a property change by returning false from the callback function.

Here is an example of using delegated properties in Kotlin:

class Example {
    var myProperty: String by MyDelegate()
}


class MyDelegate {
    private var myValue: String = ""


    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("Getting the value of ${property.name}")
        return myValue
    }


    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("Setting the value of ${property.name} to $value")
        myValue = value
    }
}

In this example, the Example class defines a property called myProperty, which is delegated to an instance of MyDelegate. The MyDelegate class defines two methods, getValue() and setValue(), which are called when the property is accessed and modified, respectively. When myProperty is accessed or modified, the appropriate method in MyDelegate is called.

--------------------

(a) lazy


The lazy delegate is used for properties that are expensive to compute and should only be computed when they are first accessed. The lazy delegate initializes the property lazily, meaning that the initial value is only calculated when the property is first accessed. This can help improve performance by avoiding unnecessary calculations.

Here's an example of how to use the lazy delegate:

val lazyProperty: String by lazy {
    println("Initializing lazy property")
    "Hello, World!"
}


fun main() {
    println("Before accessing the lazy property")
    println(lazyProperty)
    println("After accessing the lazy property")
    println(lazyProperty)
}

In this example, we define a lazyProperty with the lazy delegate. The delegate takes a lambda expression that will be executed the first time the property is accessed. In this case, the lambda expression simply prints a message to the console and returns the string "Hello, World!".

In the main() function, we first print a message before accessing the lazy property. When we access the lazyProperty for the first time, the lambda expression is executed, and the message "Initializing lazy property" is printed to the console. The value "Hello, World!" is then returned and printed to the console.

When we access the lazyProperty for the second time, the lambda expression is not executed again because the property has already been initialized. The value "Hello, World!" is simply returned and printed to the console.

So the output of the program would be:

Before accessing the lazy property
Initializing lazy property
Hello, World!
After accessing the lazy property
Hello, World!

As you can see, the lambda expression is only executed once, when the property is first accessed, and not again on subsequent accesses. This can help to improve performance in cases where the property is expensive to compute.

--------------

(b) observable


The observable delegate is used to add a listener to a property so that when the property value is changed, a callback function is called. The observable delegate takes a lambda function that is called when the property value changes.

Here's an example of how to use the observable delegate:

class Person {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("Name changed from $old to $new")
    }
}


fun main() {
    val person = Person()
    println(person.name)


    person.name = "John"
    println(person.name)


    person.name = "Jane"
    println(person.name)
}

In this example, we define a Person class with a name property. We use the Delegates.observable() method to create an observable property. The method takes an initial value for the property and a lambda expression that is called every time the property is changed.

The lambda expression takes three parameters: the property being changed, the old value of the property, and the new value of the property. In the example, we simply print a message to the console indicating that the name has changed and what the old and new values are.

In the main() function, we create an instance of the Person class and print the initial value of the name property, which is "<no name>".

We then change the name property twice, first to "John" and then to "Jane". Each time the property is changed, the lambda expression passed to Delegates.observable() is called, and the message indicating the name change is printed to the console.

So the output of the program would be:

<no name>
Name changed from <no name> to John
John
Name changed from John to Jane
Jane

As you can see, the lambda expression passed to Delegates.observable() is called every time the name property is changed, allowing us to execute custom code whenever the property changes. This can be useful in many situations, such as updating UI elements when a property changes.

---------------

(c) vetoable


The vetoable delegate is used to add a listener to a property so that when the property value is changed, a callback function is called. The vetoable delegate takes a lambda function that is called before the property value is changed, allowing the value to be vetoed and prevented from changing.

Here's an example of how to use the vetoable delegate:

class Person {
    var age: Int by Delegates.vetoable(0) {
        prop, old, new ->
        if (new < 0) {
            println("Invalid age: $new")
            false
        } else {
            true
        }
    }
}


fun main() {
    val person = Person()
    println(person.age)


    person.age = 25
    println(person.age)


    person.age = -10
    println(person.age)
}

In this example, we define a Person class with an age property. We use the Delegates.vetoable() method to create a vetoable property. The method takes an initial value for the property and a lambda expression that is called every time the property is about to change.

The lambda expression takes three parameters: the property being changed, the old value of the property, and the new value of the property. In the example, we check if the new value is less than 0. If it is, we print a message to the console indicating that the age is invalid and return false to prevent the property from being changed. Otherwise, we return true to allow the property to be changed.

In the main() function, we create an instance of the Person class and print the initial value of the age property, which is 0.

We then change the age property twice, first to 25 and then to -10. When we change the property to 25, the lambda expression is called but does not veto the change because 25 is a valid age. When we change the property to -10, the lambda expression is called and prints a message to the console indicating that the age is invalid. The property is not changed because the lambda expression returned false.

So the output of the program would be:

0
25
Invalid age: -10
25

As you can see, the lambda expression passed to Delegates.vetoable() is called every time the age property is about to change, allowing us to prevent the property from changing if necessary. This can be useful in many situations, such as validating user input before changing a property.

1
528
HashMap Operations Complexity O(N)

HashMap Operations Complexity O(N)

1675112374.jpg
Mohamad Abuzaid
1 year ago
How to create custom annotations in Kotlin

How to create custom annotations in Kotlin

1675112374.jpg
Mohamad Abuzaid
6 months ago
Effective UI/UX Design in Android Apps (3/3)

Effective UI/UX Design in Android Apps (3/3)

1675112374.jpg
Mohamad Abuzaid
7 months ago
Effective UI/UX Design in Android Apps (2/3)

Effective UI/UX Design in Android Apps (2/3)

1675112374.jpg
Mohamad Abuzaid
7 months ago
In Kotlin Coroutines...

In Kotlin Coroutines...

1675112374.jpg
Mohamad Abuzaid
1 year ago