Photo by Sigmund on Unsplash

Kotlin Delegates

A great way to reduce, reuse, and recycle your code

Jordan Hansen
Feb 16 · 5 min read

The Delegation pattern has proven to be a good alternative to implementation inheritance”. This pattern allows composition to make code just as reusable as inheritance. Kotlin has made delegation a first class language feature through the by keyword. Kotlin supports delegation in two ways: Interface implementation and property delegation. These delegates will help you reduce boilerplate code, reuse common features and patterns, and recycle existing information into even more usable ways.

When you want to add a small piece of functionality to class in object-oriented programming you would generally start by extending that class. However sometimes this isn’t possible:

  1. The base class is closed, sealed, or a data class
  2. Your class already extends another class
  3. Philosophical objection to inheritance¹

Most of the time the classes we want to extend have an interface that is the true target to which we want to add functionality. Delegation works by providing a base implementation for the interface and delegating the interface to that object.

interface Base {
fun print()
}
class Derived(private val b: Base = DefaultBaseImpl()): Base {
override fun print() { b.print() }
fun addition() {}
}

Even with just one method in the interface, you can see the boilerplate to use delegation. Just imaging trying to extend something like Channel which extends two other interfaces. Enter Kotlin for the win.

class Derived(private val b: Base = DefaultBaseImpl()): Base by b {
fun addition() {}
}

The Kotlin compiler knows by the by keyword to generate all the functions of Base and pass the implementation onto b. We are left with a class that has only the extra function we actually care about. For example:

class ViewModelChannel<E>(
private val viewModel: ViewModel,
private val channel: Channel<E> = Channel()
): ReceiveChannel<E> by channel, SendChannel<E> by channel {
fun sendAsync(element: E): Job = viewModel.viewModelScope.launch {
channel.send(element)
}
}

With 9 lines of code we now have a send and receive channel that can send elements asynchronously, and it is scoped to the lifecycle of our view model with ZERO boilerplate code.

While implementing interfaces without writing a ton of code is nice, sometimes we just need a single property to behave in a common manner. Kotlin has provided some great standard property delegates to do just this:

  • lazy properties: the value gets computed only upon first access
  • observable properties: listeners get notified about changes to this property
  • storing properties in a map, instead of a separate field for each property
  • See more standard delegates here.

But what if your needs aren’t met by these standard delegates? Properties in Kotlin are essentially one of two interfaces: val and var. A val is a generic interface with a get method, and var is a generic interface that has a get and a set method. Since properties are essentially interfaces we can implement these interfaces to create a delegate. Now you can read the full documentation on how to create delegates here, but I find it is so much simpler and less error prone to extend ReadOnlyProperty for val delegates and ReadWriteProperty for var delegates.

fun savedState(
savedStateHandle: SavedStateHandle, key: String, defaultValue: T
) = object: ReadWriteProperty(Any, T) {

override fun getValue(
thisRef: Any, property: KProperty<*>
): T {
return savedStateHandle.get(key) ?: defaultValue
}
override fun setValue(
thisRef: Any, property: KProperty<*>, value: T
) {
savedStateHandle.set(key, value)
}
}val id by savedState<Int>(savedStateHandle, "id", 0)

The above delegate stores the value of property in the SavedStateHandle making sure any time we update that property the value is saved across changes that may occur. By taking the time to write this delegate we don’t have to call get and set on the saved state handle everywhere.

Now I want to call out 2 really cool things, thisRef and property. The type of thisRef limits the class types the delegate can be used in. Any is used to denote a delegate can be used anywhere. However if you limit the scope of a delegate, let’s say to ViewModel, the delegate can access properties and methods of the containing object through thisRef, adding more context to the delegate. While thisRef references the containing object, property references the actual val or var being delegated. This is a cool object that lets us access information about the property itself like its name. By using the property argument we can simplify our delegate above to get rid of the key and utilize its name as the key².

fun savedState(
savedStateHandle: SavedStateHandle, defaultValue: T
) = object: ReadWriteProperty(Any, T) {

override fun getValue(
thisRef: Any, property: KProperty<*>
): T {
return savedStateHandle.get(property.name) ?: defaultValue
}
override fun setValue(
thisRef: Any, property: KProperty<*>, value: T
) {
savedStateHandle.set(property.name, value)
}
}val id by savedState<Int>(savedStateHandle, 0)

An amazing delegate, provided in a google architecture sample is autoCleared. AutoCleared releases its stored value to be garbage collected when the Fragment’s view is destroyed, thus avoiding memory leaks. However, what if instead of throwing an exception when a value is cleared I want the delegate to return null. I could create a new delegate method that is called autoClearedNullable that returns a new delegate that allows null to be returned. This will work, but Kotlin has an awesome feature that provides the delegate allowing developers to return different implementations or perform validation before the delegate is created. The magic is an operator named provideDelegate.

class AutoClearedLoader<T>() {
operator fun provideDelegate(
thisRef: Fragment,
property: KProperty<*>
): ReadWriteProperty<Fragment, T> {
return if (property.returnType.isMarkedNullable) {
AutoClearedNullableValue(thisRef)
} else {
AutoClearedValue(thisRef)
}
}
}
fun <T> Fragment.autoCleared() = AutoClearedLoader()class MyFragment(): Fragment { val menuItem by autoCleared<MenuItem?>()
}

provideDelegate takes 2 parameters, thisRef and property just like getValue. thisRef must be the same type or a supertype for the property owner, and you can access the property just as you would in the delegate itself. The provdeDelegate method will be called for each property during the creation of a MyFragment instance. If you are running validation, checking property names are valid, it will fail right away if there is an error. These delegate loaders provide an opportunity for developers to take advantage of information without having to redundantly specify the data.

Kotlin delegates are an amazingly powerful tool in reducing boilerplate code, reusing code in a clean and concise manner, and recycling information about properties without being redundant. Happy delegating!

  1. Just search “problems with inheritance” to see a litany of objections to inheritance.
  2. Using the name of a property to store things beyond the runtime of an application is a bad idea. One refactor removes any connection between that property and its storage key.

The Startup

Get smarter at building your thing. Join The Startup’s +792K followers.

Sign up for Top 10 Stories

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Jordan Hansen

Written by

I have been developing Android Applications professionally since 2012.

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +792K followers.

Jordan Hansen

Written by

I have been developing Android Applications professionally since 2012.

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +792K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store