· 4 min read

Property delegation in Kotlin: Assign values in Android without having the context (KAD 15)

As we’ve seen in previous articles, properties need a default value, they can’t be declared without assigning them a value.

This is a problem, because imagine that you want to store a view into a property. As this code is executed during object creation, you can’t access the context at that time.

What can you do?

If you want to start today, I recommend you take a look at my free training, where you will have an hour and a half of content to know what are your next steps to become an expert in Kotlin.

Action View

Property delegation: delegate the value of a property to another object

The property delegation will use another object that is able to return a result when calling get and set (if var is used).

In a world where we have no control over the creation of many objects, such as the Android framework, this delegation will save our lives in many cases.

I’ll show you three examples that I find very useful in Android

Setting a view to a property

For this we have two options using delegation, and prevent using null, (something not recommended if you can avoid it).

The first one I like the least, because it forces you to use var for a property that could be immutable, and is also less secure.

With the reserved word lateinit we say to that property that it’ll not be empty, but that we still don’t have its final value:

lateinit var textView: TextView

In the onCreate we can assign the final value:

setContentView(R.layout.activity_main)
textView = findView(R.id.welcomeMessage)
toast(textView.text)

This isn’t really a delegate, although it does the same operation as the delegate notNull, which was relegated in favor of this first.

The second option is much more elegant. It consists of using the lazy delegate, which won’t execute the code that is indicated until the property is called for the first time:

val textView by lazy { findView(R.id.welcomeMessage) }

The findView won’t run until the get of the textView is called for the first time. It’s safer because you can’t change the value by mistake, and it doesn’t force us to remember to set it after the setContentView.

At the moment we do:

toast(textView.text)

The code will be executed in lazy form.

As you can see, the way to delegate is by using the reserved word by.

Let’s see another example

Notify changes to an adapter

In an adapter we can have an items property that automatically launches an adapter update every time it is set:

var items: List by Delegates.observable(emptyList()) {
    _, _, _ -> notifyDataSetChanged()
}

It simply sets an initial value, and then calls the function that is defined after each modification.

In this case I’m just calling notifyDataSetChanged, but as you see, the function receives the old and new values, so technically you could check what the changes are and update only the difference.

If you’re interested in this example, I wrote about it extensively in another article.

Declare the Dagger graph in a lazy way

It’s another of the situations in which I have found this functionality very useful.

Returning to lazy, you can use it to declare the component of the application during the declaration of the property:

val component: AppComponent by lazy { 
    DaggerAppComponent
            .builder()
            .appModule(AppModule(this))
            .build() 
}

This way you don’t need to use lateinit, and the property becomes immutable.

You can do the same if you are using subcomponents in the activities:

class HomeActivity : AppCompatActivity(), HomePresenter.View {
    val component by lazy { app.component.plus(HomeModule(this)) }
    ...
}

New in Kotlin 1.1: Local delegated properties

We’ve already seen how useful delegation is to give extra abilities to the properties in our classes. But lazy for instance, would also be really helpful too on variables, and Kotlin was lacking this feature.

Now, with local delegated properties, we can do it:

fun testLocalDelegation() {
    val database by lazy { createDatabase() }
    val cache by lazy { createMemoryCache() }

    if (mustUseDatabase()) {
        database.use { ... }
    } else {
        cache.use { ... }
    }
}

Though this example could be resolved without using lazy delegation, it helps understand the concept.

We have a couple of heavy objects that may or may not be used. By using lazy, we can delay the instantiation until we are sure we are going to use them.

The first time is used, the code inside the braces is executed, and it will be cached in case it’s used again later.

Conclusion

The property delegation will help you make properties much more powerful and simplify and reuse code.

Here we have only seen standard properties of Kotlin’s library, but you can create your own in another article.

For example in the book I have one implemented that stores and retrieves data from the SharedPreference.

If you like what you’ve seen, I encourage you to sign up for my free training, where I’ll tell you everything you need to learn about how to create your own Android Apps in Kotlin from scratch.

    Share:
    Back to Blog