The Power of Kotlin Delegation

Ryan Gordon
Genetec Tech
Published in
13 min readOct 21, 2019
Image by David Mark from Pixabay

Once upon a time, inheritance was our primary means to grow and scale our applications. We identified base classes to represent common behaviour that could be extended to leverage existing assets. We built contracts and provided templates with abstract classes. We controlled behaviour and dictated strategy with polymorphism.

Then one day, we’re introduced to a new trend that’s gaining traction.

Favour Composition over Inheritance.

Some of you may relate to this, while others may already be well-versed in the ongoing debate between Inheritance vs Composition. In truth, composition is nothing new and has been around for as long as inheritance. We won’t debate the issue here aside from briefly mentioning how Kotlin may be hinting at something by making its classes closed (or final) by default; and further mentioning how other successful languages are designed in this manner.

The Delegation Pattern is a popular design pattern where a parent object will pass on requests to a child a Delegate Object. This provides code re-use that is similarly achieved via inheritance while also enforcing the “Single Responsibility Principle” which allows the parent to remain agnostic of how the request is performed.

Composition and the creation of delegates can be achieved in any language, but Kotlin facilitates this with the by keyword. It can be used for property delegation or implementation by delegation. We’ll cover both in this article.

Delegated Properties

Before exploring delegates and all Kotlin has to offer, we’ll try to develop an appreciation for them by building a use case via alternative solutions.

Life before Delegated Properties

Imagine a scenario that requires us to scramble a string data type during each read-access. Suppose we have var planetName = “Earth”, where planetName is the string to be scrambled. If we use println(planetName) as an example for reading its value, then a scrambled version of the string will be returned. It might look like “eraht” or “reaht” or “treah”. A second call to println(planetName) will output a new scrambled version which might be “ahert” or “hreat” or “ather”, and this is repeated for each execution of println(planetName) or with any other method we use for reading planetName.

To solve this, we’ll probably implement something like the following:

var planetName = "Earth"
planetName = planetName
.toList()
.shuffled()
.joinToString(separator = "")
println("My Scrambled Planet: $planetName")

Is planetName.toList().shuffled().joinToString(separator = "") the most optimal way to scramble a string?
Probably not.

Is there a more efficient way which isn’t necessarily the most optimal?
There probably is.

Putting the debate aside, we’ll carry-on with this implementation as it’s easy to understand. And because we’re good Kotlin developers, we’ll extract this into an extension function.

fun String.scramble() : String {
return this.toList().shuffled().joinToString(separator = "")
}

-

var planetName = "Earth"
planetName = planetName.scramble()
println("My Scrambled Planet: $planetName")

So far so good.

But not quite.

Our requirement is to scramble the string during each read-access. A second call to println(planetName) will only return the previous scrambled version of planetName. We must first execute planetName = planetName.scramble() to not only scramble planetName, but overwrite its previous value with the new version of the string. We need to keep track of the state of planetName in addition to performing the scramble. It would be nice if we can do all of this in a single statement.

This isn’t a hard problem to solve.

class StringScrambler(private var value: String) {
fun nextString(): String {
value = value.scramble()
return value
}
}
fun String.scramble(): String {
return this.toList().shuffled().joinToString(separator = "")
}

-

val planetName = StringScrambler("Earth")
println("My Scrambled Planet: ${planetName.nextString()}")
println("My Scrambled Planet: ${planetName.nextString()}")
println("My Scrambled Planet: ${planetName.nextString()}")

Our StringScrambler handles this nicely. Wrapping our string inside this class helps solve our state-management issue while encapsulating the logic. Everything is self-contained.

StringScrambler is an example of a Delegate. This is classical manual delegation. And to demonstrate its extensibility, we’ll expand our use case to track the count of each scramble and read-access.

Why do we need a scrambled-read-access count?
Because it’s going to be logged with the scrambled value.

Why do we need to log the scrambled-read-access count and scrambled value?
Because it’s the lead-up to something more useful like analytics tracking which is beyond the scope of this example. We’ll stick with logging for now, but feel free to use your imagination and pretend it’s for analytics.

class StringScrambler(private var value: String) {
private var count = 0
fun nextString(): String {
value = value.scramble()
count = count.inc()
println("$count: Reading new scrambled value '${value}'")
return value
}
}
fun String.scramble(): String {
return this.toList().shuffled().joinToString(separator = "")
}

-

val planetName = StringScrambler("Earth")
println("My Scrambled Planet: ${planetName.nextString()}")
println("My Scrambled Planet: ${planetName.nextString()}")
println("My Scrambled Planet: ${planetName.nextString()}")

So our StringScrambler is not only extensible, but also versatile. It solves our problem, but we can do better.

Implementing a Custom Delegated Property

Kotlin’s by keyword allows us to delegate the read-access of an object to another class. It will then be referred to as a delegated property. The syntax for this is as follows:

class MyDelegate {
operator fun getValue(ref: Any?, kProp: KProperty<*>): String {
return "My name is '${kProp.name}' and '$ref' is my parent."
}
}

Its creation begins with the implementation of the getValue() method.
Note the operator keyword.

  • ref: Any? is a reference of the property’s parent.
  • kProp: KProperty<*> is the reflection reference to obtain the property’s meta data.

It can then be used like any other property.

class MyParent {
val myDelegate: String by MyDelegate()
}

-

val myParent = MyParent()
println(myParent.myDelegate)

Or just as a variable.

val myDelegate: String by MyDelegate()
println(myDelegate)

This last one deserves special mention as local delegated properties have only been available since Kotlin 1.1. We’re a long way from version 1.1 today, but it’s worth mentioning should it ever come up during an interview, or should you ever find yourself engaged in a game of trivia.

The important takeaway is that, in both cases, we are reading the value directly without making an explicit function call.

Now having gained a basic understanding of Kotlin delegated properties, how does this transform our StringScrambler?

It will look like this:

class StringScramblerDelegate(private var value: String) {
private var count = 0
operator fun getValue(ref: Any?, kProp: KProperty<*>): String {
value = value.scramble()
count = count.inc()
println("$count: Reading '$value' from '${kProp.name}'")
return value
}
}
fun String.scramble(): String {
return this.toList().shuffled().joinToString(separator = "")
}

-

val planetName: String by StringScramblerDelegate("Earth")println("My Scrambled Planet: $planetName")
println("My Scrambled Planet: $planetName")
println("My Scrambled Planet: $planetName")

Instead of ${planetName.nextString()} to scramble, count, log, and then read our next string value, we can simply use $planetName.

So far we’ve only dealt with Read Only delegates, but the ability to write a value is also important. Recalling our previous manual StringScrambler, providing write-access can be as simple as adding a function to update the string value. The solution for this is trivial so we won’t show an example; however, we must acknowledge the need for calling this function to write similarly to the StringScrambler.nextString() we wanted to avoid for reading.

We can do better with Kotlin Delegated Properties.

To make our StringScramblerDelegate writeable, we’ll implement the setValue() method.

class StringScramblerDelegate(private var value: String) {
private var count = 0
operator fun getValue(ref: Any?, kProp: KProperty<*>): String {
value = value.scramble()
count = count.inc()
println("$count: Reading new '$value' from '${kProp.name}'")
return value
}
operator fun setValue(ref: Any?, kProp: KProperty<*>, value: String) {
count = 0 // reset count on each update
println("Setting value '$value' on '${kProp.name}'")
this.value = value
}
}

-

var planetName: String by StringScramblerDelegate("Earth")
println("My Planet: $planetName")
println("My Planet: $planetName")
println("My Planet: $planetName")
planetName = "Mercury"
println("My Planet: $planetName")
println("My Planet: $planetName")
println("My Planet: $planetName")
planetName = "Venus"
println("My Planet: $planetName")
println("My Planet: $planetName")
println("My Planet: $planetName")

Writing to our property is as seamless as reading.

Let’s take this a step further with an example of writing data to persistent storage. Android has various solutions for this: SharedPreferences, Database, File System, etc… I’m sure you have similar options for whatever framework you’re involved with. In this example, we’ll take advantage of SOLID’s Dependency Inversion Principle to keep our implementation agnostic by abstracting the dependency to an interface; and to keep this example generic, we’ll implement the concrete class as a simple stub.

interface StorageWrapper {
fun readValue(): String
fun writeValue(value: String)
}
class StubStorageWrapper : StorageWrapper {
private var value: String = ""
override fun readValue(): String {
return value
}
override fun writeValue(value: String) {
this.value = value
}
}

Here’s a working example of the delegate.

class StorageDelegate(private val storageWrapper: StorageWrapper) {    operator fun getValue(ref: Any?, kProp: KProperty<*>): String {
return storageWrapper.readValue()
}
operator fun setValue(ref: Any?, kProp: KProperty<*>, value: String) {
this.storageWrapper.writeValue(value)
}
}

-

var planetName: String by StorageDelegate(StubStorageWrapper())
planetName = "Mercury"
println("My Planet: $planetName")

Kotlin provides the following interfaces for property delegation.

interface ReadOnlyProperty<in R, out T> {
operator fun getValue(ref: R, kProp: KProperty<*>): T
}
interface ReadWriteProperty<in R, T> {
operator fun getValue(ref: R, kProp: KProperty<*>): T
operator fun setValue(ref: R, kProp: KProperty<*>, value: T)
}

Although not required, the interfaces are recommended for readability and maintainability. Here’s what it looks like with our StorageDelegate.

class StorageDelegate(
private val storageWrapper: StorageWrapper
) : ReadWriteProperty<Any?, String> {
override fun getValue(ref: Any?, kProp: KProperty<*>): String {
return storageWrapper.readValue()
}
override fun setValue(ref: Any?, kProp: KProperty<*>, value: String) {
this.storageWrapper.writeValue(value)
}
}

Our StorageDelegate is based on a String type, but we can define a type-parameter and work with it as a generic type.

Provide Delegate

The creation of our StorageDelegate and its dependent StubStorageWrapper is trivial because it was designed to be simple for the purposes of this example. A realistic scenario would require creation logic centered around preference keys, database IDs, file paths, etc… A common use case would be to hide this logic. This can be achieved by implementing the provideDelegate operator which allows us to encapsulate this creation logic while continuing to use the by keyword.

class StorageDelegateFactory {
operator fun provideDelegate(ref: Any?, kProp: KProperty<*>): ReadWriteProperty<Any?, String> {
println("Creating new StorageDelegate for '${kProp.name}'")
return StorageDelegate(StubStorageWrapper())
}
}

provideDelegate is very similar to getValue()

  • ref: Any? is a reference of the property’s parent.
  • kProp: KProperty<*> is the reflection reference to obtain the property’s meta data.

Here’s a working example of how it’s used.

class StorageDelegateFactory {
operator fun provideDelegate(ref: Any?, kProp: KProperty<*>): ReadWriteProperty<Any?, String> {
println("Creating new StorageDelegate for '${kProp.name}'")
return StorageDelegate(StubStorageWrapper())
}
}

-

var planetName: String by StorageDelegateFactory()
planetName = "Mercury"
println("My Planet: $planetName")

In addition to delegating the read/write-access of our property, we can now delegate its creation.

Standard Delegated Properties

Kotlin provides a suite of ready-to-use delegates. They’re provided by factory methods and implement the ReadOnlyProperty and ReadWriteProperty explained above.

NotNull

Have you used Kotlin’s lateinit modifier? They’re convenient for situations where a property should be declared as non-nullable but can’t be initialized on declaration. The downside, however, is that they are not allowed on primitive types.

The NotNull is one solution that can help us. Its factory method has the following definition:

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

And it’s easy to use.

var planetName : String by notNull()fun main() {
planetName = "Saturn"
println("My Planet: $planetName")
}

An IllegalStateException is thrown when attempting to read a value that has not been set.

Keep in mind that NotNull is generally more expensive than lateinit as an extra object is created for each declaration.

NotNull is also not a replacement for field injected properties, such as those from Dagger2.

NotNull is perfect for unit testing where it’s desirable to have a non-null property which must be re-initialized during each test run. Rather than initializing your property on declaration and then re-initializing it again during your @Before annotated setup method, we can make use of NotNull.

Lazy

Kotlin’s Lazy delegate is used to delay the initialization of a property until its first read-access. It can also be used as a replacement for a lateinit property, but the intended use is to delay the execution of expensive computations until they are required, and, more importantly, to avoid them altogether if they are never used. Its factory method has the following definition:

fun <T> lazy(initializer: () -> T): Lazy<T>fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T>
  • initializer is the callback function invoked on the property’s first read-access. If the execution throws an exception, it will try again on the next read-access.
  • mode specifies how the initializer callback should be synchronized.
    LazyThreadSafetyMode.SYNCHRONIZED is used by default and is generally the mode we’ll use in most scenarios. This mode ensures that only one thread can initialize the property.
    LazyThreadSafetyMode.PUBLICATION allows for concurrent access of the property during the first read initialization. Only the first returned value will be used as the property’s initialized value leaving the others to be ignored.
    LazyThreadSafetyMode.NONE specifies that nothing should be used to control synchronization. This is applicable for single-threaded environments such as Android’s Main Thread.
fun calculateStarsInUniverse(): Long {
println("The number of stars in the universe is estimated to equal the number of grains of sand on all the beaches on Earth.")
println("A very big number.")
println("We will only initialize to Long.MAX_VALUE.")
return Long.MAX_VALUE
}

-

val numStars: Long by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
calculateStarsInUniverse()
}
// Initializing on first read
println("First Read Access: Count = $numStars")
// Reading initialized value
println("Second Read Access: Count = $numStars")
println("Third Read Access: Count = $numStars")

Vetoable

The Vetoable delegate allows us to intercept the write access of a property. Its factory method has the following definition:

inline fun <T> vetoable(
initialValue: T,
crossinline onChange: (kProp: KProperty<*>, oldVal: T, newVal: T) -> Boolean
): ReadWriteProperty<Any?, T>
  • initialValue is the initial value of the property.
  • onChange is the callback invoked during a property’s write-access “before” it is written. The boolean return value specifies if the property is allowed to carry-on with the update to the newVal or if it will be “vetoed” to keep the current oldVal.

A common use case for Vetoable is for validation which comes in many forms: restricting an integer to a certain range, enforcing decimal format on a string to represent a currency, etc...

Advances in science and astronomy over the past few decades has changed the landscape of our Solar System. Let’s write a delegate that enforces the International Astronomical Union’s 24 August 2006 vote for the reclassification of planets.

private var planetName: String by Delegates.vetoable("Earth") { kProp, oldVal, newVal ->
val isValid = newValue.toLowerCase() != "pluto"
if (isValid) {
println("Update ${kProp.name} from '$oldVal' to '$newVal'")
} else {
println("Veto Update on ${kProp.name}'")
}
isValid
}

planetName = "Mars"
println("My Planet: $planetName")
planetName = "Neptune"
println("My Planet: $planetName")

// This update will be vetoed
planetName = "Pluto"
println("My Planet: $planetName")
planetName = "55 Cancri e"
println("My Planet: $planetName")

Observable

The Observable delegate notifies us every time its value has changed. Its factory method has the following definition:

inline fun <T> observable(
initialValue: T,
crossinline onChange: (kProp: KProperty<*>, oldVal: T, newVal: T) -> Unit
): ReadWriteProperty<Any?, T>
  • initialValue is the initial value of the property.
  • onChange is the callback invoked during a property’s write-access “after” it is written.

This is a throw-back to the classic and ubiquitous observer pattern used to drive events from an object’s change in state.

private var planetName: String by Delegates.observable("Earth") { kProp, oldVal, newVal ->
println("${kProp.name} was updated from '$oldVal' to '$newVal'")
}
planetName = "Uranus"
planetName = "Jupiter"

Setting Properties from a Map

How would you convert items from a map to the properties of an object? One solution would involve iterating over the keys and manually setting the value to each property. A better solution would be to use the map instance as a delegate.

data class Satellite(val name: String, val discoveryDate: LocalDate)val planetData = mapOf(
"name" to "Jupiter",
"adjective" to "Jovian",
"distance" to 5.20f,
"characteristics" to mapOf(
"orbitalPeriod" to 11.82,
"rotationPeriod" to 9.93,
"volume" to 1321
),
"totalSatellites" to 79,
"galileanSatellites" to listOf(
Satellite("Ganymede", LocalDate.of(1610, 1, 7)),
Satellite("Callisto", LocalDate.of(1610, 1, 7)),
Satellite("Europa", LocalDate.of(1610, 1, 8)),
Satellite("Io", LocalDate.of(1610, 1, 8))
)
)
val name: String by planetData
val adjective: String by planetData
val distance: Float by planetData
val characteristics: Map<String, Any> by planetData
val totalSatellites: Int by planetData
val galileanSatellites: List<Satellite> by planetData

-

println("$name is $distance AU from the Sun.")

println("A $adjective day is ${characteristics["rotationPeriod"]} hours")

println("A $adjective year is ${characteristics["orbitalPeriod"]} Earth years.")

println("You can fit ${characteristics["volume"]} Earths inside $name.")
println("There are $totalSatellites $adjective satellites.")

val period = Period.between(galileanSatellites[0].discoveryDate, LocalDate.now())
println(
"""The Galilean Satellites: ${galileanSatellites.map { it.name }}
|were discovered by Galileo Galilei
${period.years} years ago
|and were recognized as the first objects to orbit another planet.
"""
.trimMargin())
}

In addition to mapping primitives, we can map lists, complex types, and nested maps.

Class Delegation

The by keyword can be used to delegate the implementation of an interface to another class. Let’s see how this is useful.

Imagine we have the following interface

data class PlanetData(val name: String, val distance: Float)interface PlanetDatabase {
fun getFirstPlanet(): PlanetData
fun getLastPlanet(): PlanetData
fun randomPlanet(): PlanetData
}

With this as its implementation

class PlanetDatabaseImpl : PlanetDatabase {
private val planets = listOf(
PlanetData("Mercury", 0.39f),
PlanetData("Venus", 0.72f),
PlanetData("Earth", 1.00f),
PlanetData("Mars", 1.52f),
PlanetData("Jupiter", 5.20f),
PlanetData("Saturn", 9.31f),
PlanetData("Uranus", 19.22f),
PlanetData("Neptune", 30.07f)
)
override fun getFirstPlanet() = planets.first()
override fun getLastPlanet() = planets.last()
override fun randomPlanet() = planets.random()
}

We now have a database that’s ready for use. But this database will be implemented as part of SolarSystemRepository and will exist among a collection of other similar databases. How should we include PlanetDatabase?

Classical inheritance like

class SolarSystemRepository : PlanetDatabaseImpl()

will rob the opportunity of other databases from being inherited. Also, classes in Kotlin are closed by default. Opening it is possible, but not very nice.

Direct composition is another solution.

class SolarSystemRepository {
val planetDatabase : PlanetDatabase = PlanetDatabaseImpl()
}

Treating it as a nested object will work, but we can do better.

Let’s make use of the by keyword and delegate the implementation of PlanetDatabase to PlanetDatabaseImpl.

The syntax for this will be

class SolarSystemRepository : PlanetDatabase by PlanetDatabaseImpl()

Here’s a working example.

private const val ASTRONOMICAL_UNIT_TO_LIGHT_MINUTES = 8.32167464fclass repository : PlanetDatabase by PlanetDatabaseImpl()// Note the different ways we can declare our references
val repository = SolarSystemRepository()
val database : PlanetDatabase = SolarSystemRepository()
// This will not compile
// val databaseImpl : PlanetDatabaseImpl = SolarSystemRepository()

-

val firstPlanet = repository.getFirstPlanet()
println("${firstPlanet.name} is the closest planet to the sun at ${firstPlanet.distance} AU")

-

val lastPlanet = repository.getLastPlanet()
println("${lastPlanet.name} is the farthest planet from the sun at ${lastPlanet.distance} AU")

-

val randomPlanet = database.randomPlanet()
val lightTime = randomPlanet
.distance
.times(ASTRONOMICAL_UNIT_TO_LIGHT_MINUTES)
println("The Planet of the day is ${randomPlanet.name}")
println("It takes $lightTime minutes for light to reach ${randomPlanet.name} at ${randomPlanet.distance} AU")

This works beautifully as it gives a direct reference to our PlanetDatabase implementation without the need for referencing a nested object. More importantly, this allows us to delegate the implementation of additional interfaces. We have in fact simulated multiple inheritance.

But we need to be aware of the following caveats:

  • An instance of the class is an instance of the interface it implements but not an instance of the delegate that implements the interface.
  • You can override and implement your version of a method — the compiler will use your version instead of the implementation provided by the delegate — but you cannot call the base implementation via super.
    You also cannot provide an extension function of the same name. It will be ignored.
  • A class may implement multiple interfaces that include the same method, but it can only delegate this implementation to a single delegate.
    The provided delegate does not need to implement each interface, but must implement the interface that follows the by keyword.

Final Thoughts

Property Delegation and Implementation by Delegation are powerful features provided by Kotlin. I hope this article has motivated you to take advantage of them.

If you’re interested in learning more about this for other languages, check out the following article on Property Wrappers for an understanding on how this is handled in Swift.

--

--

Ryan Gordon
Genetec Tech

I’m an Android developer with a passion for creative writing.