Delegating Delegates to Kotlin

Murat Yener
Android Developers
Published in
5 min readOct 7, 2020

--

Kotlin Vocabulary: Delegates

One way to get some work done is to delegate that work to another party. No, I am not talking about delegating your work to your friend, but delegating work from one object to another.

Guess what, delegation is not new to software. Delegation is a design pattern in which an object handles a request by delegating to a helper object, called the delegate. The delegate is responsible for handling the request on behalf of the original object and making the results available to the original object.

Kotlin makes delegation easier by providing support for class and property delegates and even containing some built-in delegates of its own.

Class Delegates

Let’s say you have a use case for an ArrayList that can recover its last removed item. Basically, all you need is the same ArrayList functionality with a reference to the last removed item.

One way to do this is to extend the ArrayList class. Since this new class is extending the concrete ArrayList rather than implementing the MutableList interface, it is highly coupled with the concrete ArrayList implementation.

Wouldn’t be nice if you could override the remove() function to keep a reference of the deleted item and delegate the rest of the empty implementations of MutableList to some other object. Kotlin provides a way to achieve this by delegating most of the work to an internal ArrayList instance and customizing its behavior. To do this, Kotlin introduces a new keyword: by.

Let’s see how class delegation works. When you use the by keyword, Kotlin automatically generates the code to use the innerList instance as a delegate.

The by keyword tells Kotlin to delegate functionality from the MutableList interface to an internal ArrayList instance named innerList. ListWithTrash still supports all the functions in the MutableList interface by providing direct bridge methods to the internal ArrayList object. Plus, now you have the ability to add your own behavior.

Under the hood

Let’s see how this works. If you take a look at the decompiled Java code from the ListWithTrash byte code, you’ll see that Kotlin compiler actually creates wrapper functions that call corresponding functions on the internal ArrayList object.

Note: The Kotlin compiler uses another design pattern called the Decorator Pattern to support class delegation in the generated code. In the decorator pattern, the decorator class shares the same interface with the class to be decorated. The decorator class keeps an internal reference of the target class and wraps, or decorates, all the public methods provided with the interface.

Delegates are especially useful when you can’t inherit from a particular class. With class delegation, your class is not part of any class hierarchy. Instead, it shares the same interface and decorates the internal object of the original type. This means you can easily switch the implementation without breaking the public API.

Delegating properties

Besides class delegation, you can also use the by keyword to delegate properties. With property delegation, the delegate is responsible for handling calls to the get and set functions of the property. This can be extremely useful if you need to reuse the getter/setter logic across other objects and lets you easily extend functionality beyond simple backing fields.

Let’s imagine you have a Person class that is defined like this:

class Person(var name: String, var lastname: String)

The name property of this class has some formatting requirements. When the name is set, you want to make sure the first letter is capitalized while formatting the rest in lowercase. Also, when updating the name, you want to automatically increment the update count property.

You could implement this functionality as shown here:

While this works, what if the requirements change and you want to increment updateCount each time the lastname changes, too? You could copy/paste the logic to write a custom setter, but suddenly you’d find yourself writing the same exact setter for each property:

Both setter methods are almost identical, telling you one of them shouldn’t exist. With property delegation, we can reuse code by delegating getters and setters to a property.

Just like class delegation, you can use by to delegate a property, and Kotlin will generate the code to use the delegate when you use property syntax.

With this change, you’ve delegated the name and lastname properties to the FormatDelegate class. Now let’s take a look at the code for FormatDelegate. The delegate class needs to implement ReadProperty<Any?, String> if you need to delegate only the getter, or ReadWriteProperty<Any?, String> if you need to delegate both the getter and the setter. In our case, FormatDelegate needs to implement ReadWriteProperty<Any?, String> since you want to perform the formatting when the setter is invoked.

You may have noticed there are two additional parameters in the getter and the setter functions. The first parameter is thisRef and represents the object that contains the property. thisRef can be used for accessing the object itself for purposes such as checking other properties or calling other class functions. The second parameter is KProperty<*>, which can be used for accessing metadata on the delegated property.

Looking back at the requirement, let’s use thisRef to access and increment the updateCount property.

Under the hood

To understand how this works, let’s take a look at the decompiled Java code. The Kotlin compiler generates both the code to keep private references of the FormatDelegate object for name and lastname properties along with getters/setters that contain the logic you’ve added.

The compiler also creates a KProperty[] to store the delegated properties. If you take a look at the generated getters and setters for the name property, the instance is stored at index 0 while the lastname property is stored at index 1.

With this trick, any caller can access the delegated property with regular property syntax.

person.lastname = “Smith” // calls generated setter, increments count

println(“Update count is $person.count)

Kotlin not only supports delegates but also provides built-in delegates in the Kotlin Standard Library, which we’ll see in more detail in another article.

Delegates can help you delegate tasks to other objects and provide better code reuse. The Kotlin compiler creates the code to let you use delegates seamlessly. Kotlin uses simple syntax with the by keyword to delegate a property or a class. Under the hood, the Kotlin compiler generates all the necessary code to support delegation without exposing any change to the public API. Simply put, Kotlin generates and maintains all the needed boilerplate code for delegates, or in other words, you can delegate your delegates to Kotlin.

--

--