Android – Understanding Property Delegation In Kotlin

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, We will learn about Property Delegation concepts in Kotlin. The Kotlin programming language has native support for class properties. Properties are usually backed directly by corresponding fields, but it does not always need to be like this – as long as they are correctly exposed to the outside world, they still can be considered properties. This can be achieved by handling this in getters and setters, or by leveraging the power of Delegates.

We will discuss the below topics to understand the Property Delegation Concepts in Kotlin:

  • What is Property Delegation?
  • What are Default Delegated Properties in Kotlin?
  • How to create a custom Delegation Property?
  • How to use Kotlin Delegation Property with Android Development?

A famous quote about Learning is :

” The beautiful thing about learning is that nobody can take it away from you.”


So Let’s begin.


What is Property Delegation?

A “Delegate” is just a class that provides the value of a property and handles its changes. This will help us to delegate(assign or pass on)the getter-setter logic altogether to a different class so that it can help us in reusing the code.

Simply put, delegated properties are not backed by a class field and delegate getting and setting to another piece of code. This allows for delegated functionality to be abstracted out and shared between multiple similar properties – e.g. storing property values in a map instead of separate fields.

Delegated properties are used by declaring the property and the delegate that it uses. The by keyword indicates that the property is controlled by the provided delegate instead of its own field.

This is an alternative to inheritance property.


What are Default Delegated Properties in Kotlin?

Kotlin contains some inbuilt examples for Delegated Properties such as:

  • lazy properties: the value gets computed only upon first access;
  • observable properties: listeners get notified about changes to this property;
  • storing properties in a map, instead of a separate field for each property.


How to create a custom Delegation Property?

For the case of simplicity, let’s take a very simple use-case. Let’s consider a scenario where we want a String property that always gets trimmed and has a common appended string after the original value.

The general way we would do this is:

var string: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
fun main() {
    string = "checking.....        "
    println(string)
}

//output: checking..... is a String!

We can see that the extra spaces are trimmed and the required string is appended.

Well, this is good for one variable. What if we have a bunch of string variables containing the same functionality?

We have to keep adding this setter property repeatedly:

var stringOne: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
var stringTwo: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
var stringThree: String = ""
    set(value) {
        field = "${value.trim()} is a String!"
    }
.
.
.
.

This meets our requirement but we see a lot of repetitive code. So, how can we resolve this?

Yes, We can resolve by Property Delegation!

Here, we shall “delegate” the property of trimming and appending the common string to a separate class so that we can reuse this wherever required.

There will be times that we want to write our delegates, rather than using ones that already exist. This relies on writing a class that extends one of two interfaces – ReadOnlyProperty or ReadWriteProperty.

Both of these interfaces define a method called getValue – which is used to supply the current value of the delegated property when it is read. This takes two arguments and returns the value of the property:

  • thisRef – a reference to the class that the property is in
  • property – a reflection description of the property being delegated

The ReadWriteProperty interface additionally defines a method called setValue that is used to update the current value of the property when it is written. This takes three arguments and has no return value:

  • thisRef – A reference to the class that the property is in
  • property – A reflection description of the property being delegated
  • value – The new value of the property

Let’s understand by String Trimming Example below as step by step .

  • Let’s create a custom class, let’s name it TrimAppendDelegate
  • To use this class as a Delegate, we have to implement the ReadWriteProperty<> interface
  • Once we implement the interface, we have to implement the abstract members in the interface which are getValue and setValue
  • Finally, we define a private variable of String type(since we are defining a custom Delegate for our String property) inside our Delegate class and define the getValue and setValue properties.
class TrimAppendDelegate : ReadWriteProperty<Any, String> {
    private var trimAppendedString = ""
    override fun getValue(thisRef: Any, property: KProperty<*>) =   trimAppendedString

override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
        trimAppendedString = "${value.trim()} is a String!"
    }
}
}

//Usage :

private var trimmedString by TrimAppendDelegate()
trimmedString = "This..      "
println(trimmedString)

//Output : This.. is a String!

This is how we can achieve the property of Delegation.


How to use Kotlin Delegation Property with Android Development?


Using Built-in delegated property Lazy:

Most of the time, we see the usage of lateinit var in our UI(Activities/Fragments) classes or View Models. We can use the concept of the lazy delegate in place of lateinit var. The variable will be initialized the first time it is used.


Using Built-in delegated property Observable:

  • Most of our Android applications use Recycler views. We know that every recycler view is associated with its respective adapter. Every time the data structure (list of objects) changes in the adapter, we call notifyDataSetChanged to update our Recycler view.
  • We can use the inbuilt delegated property “Observable” to get the old and changed values of the data structure(list of objects)
private var users: ArrayList<User> by Delegates.observable(arrayListOf()) { property, oldValue, newValue ->
    Log.d("Old value ${property.name} size", "${oldValue.size}")
    Log.d("New value ${property.name} size", "${newValue.size}")
    notifyDataSetChanged()
}

In the above code snippet, we can consider User as one of the model classes, and “users” is a list of User objects. We can access the old and new values whenever the value for “user” changes. Finally, we can call notifyDataSetChanged, if there is a change in oldValue and newValue by comparison.

To access the advantage of this Observable delegated property, the parameter should be a “var” instead of “val”. Else the changes cannot be identified, because, val, in Kotlin, is read-only.


Using Built-in delegated property Observable along with Lazy:

We can also use this “Observable” property along with “lazy” for updating our views. This can be very helpful if we are not using the concept of LiveData in our application.

Let’s understand the code snippet :

class MainActivity : AppCompatActivity() {
    
    private val textView: TextView by lazy { textview }
    private var text: String by Delegates.observable("") { _, _, newValue ->
        textView.text = newValue
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        text = "NewText"
    }
    
}

In the above code snippet, We have initiated textView with a lazy delegate just for our safety so that there will be no null pointer exception while accessing it. Whenever there is a change in the “text” variable, the text view is updated with the new value. This is similar to the concept of Live Data.


Using Built-in delegated property Lazy along with Custom Delegate:

Let’s say we want to save a boolean shared preference in our MainActivity.kt file (Any activity class file). Let’s create a custom delegate to store a Boolean shared preference value:

//Delegating the boolean preference saving option
class BooleanPreference(
    private val preferences: Lazy<SharedPreferences>,
    private val name: String,
    private val defaultValue: Boolean
) : ReadWriteProperty<Any, Boolean> {
    
    @WorkerThread
    override fun getValue(thisRef: Any, property: KProperty<*>): Boolean {
        return preferences.value.getBoolean(name, defaultValue)
    }
    override fun setValue(thisRef: Any, property: KProperty<*>, value: Boolean) {
        preferences.value.edit().putBoolean(name, value).apply()
    }
    
}

We can see that our custom delegate class takes three parameters, the preferences instance, the name(Key in this case), and the default value.

So, let’s create our shared preferences global instance in our Activity file.

private val prefs: Lazy<SharedPreferences> = lazy { // Lazy to prevent IO access to main thread.
    this.applicationContext.getSharedPreferences(
        PREFS_NAME, MODE_PRIVATE
    )
}
companion object {
    const val PREFS_NAME = "Preferences"
    const val TOGGLE_PREFS = "toggle"
}

Let’s say we are handling the preference of a toggle here in our Activity class. We can just use our custom delegate as follows:

private var togglePreference by BooleanPreference(prefs, TOGGLE_PREFS, false)

Now, let’s say if we want to change the value in the onCreate method(or any click listener, in general):

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    togglePreference = true
    Toast.makeText(this, "$togglePreference", Toast.LENGTH_SHORT).show()  // Shows true
}

We just use it as a simple variable assignment. Looks clean and concise!

That’s all about in this article.


Conclusion

In this article, We understood about Property Delegation concepts in Kotlin. We have discussed about What is Property Delegation, Default Delegated Properties, create Custom Property Delegation and how to use Property delegation with Android Development. Property delegation is a powerful technique, that allows you to write code that takes over control of other properties, and helps this logic to be easily shared amongst different classes. This allows for robust, reusable logic that looks and feels like regular property access.

Thanks for reading ! I hope you enjoyed and learned about Property Delegation concepts in Kotlin. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Loading

Leave a Comment