Built-in Delegates

Murat Yener
Android Developers
Published in
5 min readNov 9, 2020

--

Kotlin Vocabulary: Delegates part 2

Delegates help you delegate tasks to other objects and provide better code reuse which you can learn more about in this article. Kotlin not only supports an easy way to implement delegates with by keyword but also provides built-in delegates such as, lazy(), observable(), vetoable() and notNull(), in the Kotlin Standard Library. Let’s see these built-in delegates and how they work under the hood.

lazy()

The lazy() function is a property delegate that helps you to lazily initialize properties, that is when they are first accessed. lazy() can be really helpful for objects which are expensive to create.

lazy() takes two parameters, a LazyThreadSafetyMode enum value and a lambda.

The LazyThreadSafetyMode parameter specifies how the initialization is synchronized between different threads, and lazy() uses a default value of LazyThreadSafetyMode.SYNCHRONIZED, which means the initialization is thread safe at the cost of a slight performance impact from explicit synchronization.

The lambda is executed when the property is accessed for the first time, and its value is then stored for future access.

Under the hood

When checking the decompiled Java code, we see that the Kotlin compiler creates a reference of type Lazy for the lazy delegate.

This delegate is initialized by the LazyKt.lazy() function with the lambda you specified and with the thread-safety mode parameter.

Let’s take a look at the source code of lazy(). Since the lazy() function uses LazyThreadSafetyMode.SYNCHRONIZED by default, it returns a Lazy object of type SynchronizedLazyImpl class.

public actual fun <T> lazy(initializer: () -> T): Lazy<T> =
SynchronizedLazyImpl(initializer)

When the delegated property is first accessed, the getValue() function of SynchronizedLazyImpl is called, which initializes the property in a synchronized block.

override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}

return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}

This guarantees that the lazy object is initialized in a thread-safe manner but adds the performance cost of the synchronized block.

Note: If you are sure that this resource will be initialized by a single thread, you can pass LazyThreadSafetyMode.NONE to lazy(), and the function will not use the synchronized block during lazy initialization. However, keep in mind that LazyThreadSafetyMode.NONE doesn’t change the synchronous nature of lazy initialization. Since lazy initialization is synchronous, it still takes the same amount of time as non-lazy initialization of the object on the first access. This means objects which take long to initialize can still block the UI thread if accessed from it.

val lazyValue: String by lazy(LazyThreadSafetyMode.NONE) {“lazy”}

Lazy initialization can be helpful to initialize expensive resources. However, for simple objects such as a String, lazy() adds overhead by generating other objects such as Lazy and KProperty.

Observable

Delegates.observable() is another built-in delegate of the Kotlin Standard Library. Observer is a design pattern in which an object maintains a list of its dependents, called observers, and notifies them automatically when its state changes. This pattern can be really helpful when more than one object needs to be informed when a value has changed, rather than having each dependent object periodically call and check if the resource is updated.

observable() takes two parameters: the initial value and a listener handler that will be called when the value is modified. observable() creates an ObservableProperty object that executes the lambda you pass to the delegate each time the setter is called.

Looking at the decompiled Person class, we see that the Kotlin compiler generates a class that extends the ObservableProperty. This class also implements a function called afterChange() that has the lambda function which is passed to the observable delegate.

The afterChange() function is invoked by the setter of the parent ObservableProperty class. This means each time a caller sets a new value for address, the setter will automatically invoke the afterChange() function, resulting in all the listeners to be notified about the change.

You can also see a call to beforeChange() in the decompiled code. beforeChange() is not used by the observable delegate but is the basis of the delegate which is coming up next, vetoable().

vetoable

vetoable() is another built-in delegate in which the property delegates veto rights to its value. Similar to the observable() delegate, vetoable() takes two parameters: the initial value and a listener handler that will be called when any caller wants to modify the value of the property.

If the lambda condition returns true, the property value will be modified, else the value will stay the same. In this case, if the caller tries to update the address with anything smaller than 15 characters, the current value will be preserved.

Looking at the decompiled Person class, Kotlin generates a new class which extends the ObservableProperty. The generated class includes the lambda we passed in beforeChange() function, which will be called by the setter before the value is set.

notNull

The last built-in delegate the Kotlin Standard Library offers is Delegates.notNull(). notNull() simply allows a property to be initialized at a later time. notNull() is similar to lateinit. In most cases, lateinit is preferred since notNull() creates an extra object for each property. However, you can use notNull() with primitive types, which lateinit doesn’t support.

val fullname: String by Delegates.notNull<String>()

notNull() uses a special type of ReadWriteProperty named NotNullVar.

Looking at the decompiled code, the fullname property is initialized with the notNull() function.

this.fullname$delegate = Delegates.INSTANCE.notNull();

This function returns a NotNullVar object.

public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()

The NotNullVar class simply holds a generic nullable internal reference and throws IllegalStateException() if any code calls the getter before the value is initialized.

The Kotlin Standard Library provides a set of built-in delegates, so you don’t need to write, maintain, and reinvent them. Built-in delegates initialize fields lazily, allow primitive types to be initialized later, observe and get notified when a value changes, and even veto those changes.

--

--