Idiomatic Kotlin: Property Delegates and Lazy
This article is a part of the Idiomatic Kotlin series. The complete list is at the bottom of the article.
Today, we will be exploring Kotlin’s property delegate and its possible purposes. This is a part of Kotlin’s first class language support for delegation. Class delegation article is available here.
What is Property Delegation?
Property delegation acts the same way as class delegation but this time, on Kotlin properties. The class owning the property can delegate the property accessor calls to a delegate object instead of handling it by itself.
Motivation
There are useful benefits in using property delegation, the most common being lazy initialization. Lazy initialization allows you to defer creation of objects until the moment they are needed. Let us see how to implement lazy initialization manually.
To implement a lazy initialized property, you have to define a backing field that can be initialized anytime. You can use a lateinit var
or a nullable type it depends on your preference. Then, manually override the property accessor methods and add your initialization logic. Note that you have to do this to every property you want to lazily initialized. With Kotlin’s property delegation and lazy initialization first class language support, you can now say goodbye to this boilerplate code.
How to create a Property Delegate?
To understand property delegation, you have to be aware that a property in Kotlin is composed of accessor methods and an optional backing field. These methods, called get
and set
, are automatically generated for you if you do not override them. To implement a property delegate, be aware that the get
and set
methods are mapped to the getValue
and setValue
operators so you need to overload them. Luckily for us, there are predefined property delegate interfaces that we can use such as ReadOnlyProperty
for val
and ReadWriteProperty
for var
. Let’s take a look at an example.
The name property is delegated to an instance of the NameDelegate
class. The NameDelegate
class uses a Database
instance to query for the name. Let us examine the NameDelegate
up close.
The NameDelegate
implements the ReadOnlyProperty
interface. The ReadOnlyProperty
interface overloads the getValue
operator method only so you wont have to define the operator
keyword on your own. Now, the getValue
has two parameters, an instance of the reference class in which the property is defined. Here, it is a generic type so it can be usable for different classes. The second parameter is of type KProperty
. KProperty
is a class that represents a Kotlin property. You do not have to actually focus on the parameters and what they can do for now, what is important is to familiarize yourself with the operator function definition.
Notice that the custom delegate has no backing field. If you want to store the current value, you have to define a backing field explicitly. Our example does not need a backing field because the getValue
is delegated to a database query.
Property delegation under the hood
Let’s see how Kotlin compiles a property delegate.
Kotlin still creates an instance of the delegate and stores it in a field. The property accessor methods are also delegated to the delegate via a direct getValue
invocation. Good old fashion Java delegation.
Lazy Initialization
Kotlin standard library already provides us with a lazy delegate. The lazy
method accepts a lambda that serves as the property initializer. Initialization is only performed on the first getValue
invocation. The value that was created will be returned on successive calls to getValue
.
The manual lazy code from above can now be replaced by a single line.
There are other benefits to using the lazy method other that boilerplate code reduction. Thread-synchronization is guaranteed as well and can be configured to your liking.
Bonus: Observables
Kotlin also provides a read/write delegate that can execute a lambda when the setValue
method is invoked. This “observable” delegate is useful for cases such as databinding and reactive programming. The syntax for this delegate is as follows:
Check out the other articles in the idiomatic kotlin series. The sample source code for each article can be found here in Github.
- Extension Functions
- Sealed Classes
- Infix Functions
- Class Delegation
- Local functions
- Object and Singleton
- Sequences
- Lambdas and SAM constructors
- Lambdas with Receiver and DSL
- Elvis operator
- Property Delegates and Lazy
- Higher-order functions and Function Types
- Inline functions
- Lambdas and Control Flows
- Reified Parameters
- Noinline and Crossinline
- Variance
- Annotations and Reflection
- Annotation Processor and Code Generation