Codementor Events

Delegated Properties in Kotlin

Published Jun 22, 2018

This tutorial written by Aanand Shekhar Roy and Rashi Karanpuria, explores delegated properties, a major update in the Kotlin 1.1 release. Aanand Shekhar Roy and Rashi Karanpuria are also the authors of Kotlin Programming Cookbook that explore android programming and web development by understanding the concepts of Kotlin programming.

Kotlin has been eating up the Java world. It has already become a hit in the Android Ecosystem (which was dominated by Java) and has been welcomed with open arms everywhere. It should be noted at this point that Kotlin is not just limited to Android development and can be used to develop server-side, client-side web applications. Also, Kotlin is 100% compatible with the JVM so you can use any existing frameworks such as Spring Boot, Vert.x, or JSF for writing Java applications.

The release of Kotlin 1.1 version has helped with the uptake of Kotlin in many ways. It included several significant updates; one of the most important ones was delegated properties.

Working with delegated properties

There are three types of delegated properties, which have now been put into a library so that they can be implemented every time we need them. They are:

• lazy: Lazy properties are the ones evaluated first and the same instance is returned after them, much like a cache
• observable: The listener is notified whenever a change is made
• map: Properties are stored in the map instead of in every field

In this article, we will look at how to work with these delegates. So, let's get started!

Getting ready

We will be working on Android code, so you will require Android Studio 3.

How to do it...

Let's look at a simple example of a delegated property:

  1. First, we will work with the lazy delegate property. Simply put, this delegate can suspend the object creation until we access it for the first time. This is really important when you are working with heavy objects; they take a long time to be created—for example, when creating a database instance or maybe dagger components. Not only this, the result is remembered and the same value is returned for subsequent calls for getValue() on this kind of delegated property. Let's take a look at an example:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val button by lazy { findViewById<Button>(R.id.submit_button) } setContentView(R.layout.activity_main)
button.text="Submit"
}

  1. The preceding is a standard onCreate method of an activity. If you look carefully, we have set the buttonvariable before the setContentView(..) method. When you run it, it runs perfectly. If you hadn't used lazy, it would have given a NullPointerException, something like this:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setText(java.lang.CharSequence)' on a null object reference

  1. The button variable was null, as we called it before the setContentView. However, this wasn't a problem with a lazily created button object, because although we had declared it before setContentView, the button object wasn't created. It was created after its first access, that is, when we tried to set a property on it.

  2. So, with a lazy construct, you don't need to think about where to place your code for initialization, and initialization of an object is deferred until its first use.

Another key thing to note is that, by default, the evaluation of lazy properties will be synchronized, which means the value is computed in one thread, and the rest of the threads will see the same value. There are three types of initialization:

• LazyThreadSafetyMode.SYNCHRONIZED: This is the default mode and ensures that only a single thread can initialize the instance.
• LazyThreadSafetyMode.PUBLICATION: In this mode, multiple threads can execute the initialization.
• LazyThreadSafetyMode.NONE: This mode is used when we are sure that initialization will happen only on one thread. For example, in the case of Android, we can be sure that views will be initialized by the UI thread only. Since this doesn't guarantee thread-safety, it has much less overhead.

Another useful delegate is the observable delegate. This delegate helps us observe any changes to the property. For example, let's take a look at a very basic implementation of the observable delegate:

fun main(args: Array<String>) {
val paris=Travel()
paris.placeName="Paris"
paris.placeName="Italy"
}
class Travel {
var placeName:String by Delegates.observable("<>"){
property, oldValue, newValue ->
println("oldValue = $oldValue, newValue = $newValue")
}
}

This is the output:

oldValue = <>, newValue = Paris
oldValue = Paris, newValue = Italy

As we can see, the observable delegate takes in two things: a default value (which we specified as <>) and a handler, which gets called whenever that property is modified.

Let's now work with the vetoable delegate. It's a lot like the observable delegate, but by using it we can "veto" the modification. Take a look at this example:

fun main(args: Array<String>) {
val paris=Travel()
paris.placeName="Paris"
paris.placeName="Italy"
println(paris.placeName)
}
class Travel {
var placeName:String by Delegates.vetoable("<>"){
property, oldValue, newValue ->
if(!newValue.equals("Paris")){
return@vetoable false
}
true
}
}

This is the output:

Paris

As you can see in the preceding example, if newValue isn't equal to "Paris", we will return false, and the modification will be aborted. If you want the modification to take place, you need to return true from the construct.

Sometimes, you create an object based on values dynamically, for example, in the case of parsing JSON. For those applications, we can use the map instance itself as the delegate for a delegated property. Let's see an example here:

fun main(args: Array<String>) {
val paris=Travel(mapOf(
"placeName" to "Paris"
))
println(paris.placeName)
}
class Travel(val map:Map<String,Any?>) {
val placeName: String by map
}

Here's the output:

Paris

To make it work for var properties, you need to use a MutableMap, so the preceding example might look something like this:

fun main(args: Array<String>) {
val paris=Travel(mutableMapOf(
"placeName" to "Paris"
))
println(paris.placeName)
}
class Travel(val map:MutableMap<String,Any?>) {
var placeName: String by map
}

Of course, the output will be the same.

There's more...

The observable delegated property can also be used extensively in adapters. Adapters are used to populate data in some sort of list. Usually, when data is updated, we just update the member variable list in the adapter and then call notifyDatasetChanged(). With the help of observable and DiffUtils, we can just update the things that are actually changed, rather than changing everything. This results in much more efficient performance.

Kotlin Programming Cookbook

If you want to learn more about Android programming and web development by understanding the concepts of Kotlin Programming, try Kotlin Programming Cookbook by Aanand Shekhar Roy and Rashi Karanpuria. This book provides quick solutions to common problems encountered during Android app development.

9781788472142cov.. (1).png

The author Aanand Shekhar Roy is a freelance Android developer. His mobile engineering career includes working for various startups and companies, such as Netherlands-based ConceptOffice and U.S.-based startups Consciously.Life and NextFan-FantasyIndyCar.

The co-author Rashi Karanpuria is a professional Android developer. She started her career by working on a wallet app for a New Zealand based Fintech startup. She has developed projects in various domains such as IOT, AdTech, business, social, and live streaming apps.

Discover and read more posts from PACKT
get started