Other Stock Property Delegates

There are a few classes in the Kotlin standard library, besides Lazy, that are set up to serve as property delegates.

Map and MutableMap

Map has the getValue() function needed to serve as a val property delegate, so we can delegate a property to a Map:

  val stuff = mapOf("something" to 1337, "somethingElse" to "like, whatever")
  val something: Int by stuff
  val somethingElse: String by stuff

  println(something)
  println(somethingElse)

Here, we use by stuff to delegate our properties to a Map named stuff. When we go to read those properties, the Map will look up the corresponding values, based on the property names, and return them.

Delegates

The Delegates class offers additional property delegates.

For example, Delegates.observable() allows you to provide a callback that will be invoked when the property value changes:

import kotlin.properties.Delegates

fun main() {
  var observed: String by Delegates.observable("initial value") { property, oldValue, newValue ->
    println("'${property.name}' changed from '$oldValue' to '$newValue'")
  }

  println(observed)

  observed = "new value"

  println(observed)
}

Your callback receives the old and new property value, along with an object representing the property itself. That is a KProperty object, which we use here to get the property name.

There is also vetoable():

import kotlin.properties.Delegates

fun main() {
  var noOddValuesPlease: Int by Delegates.vetoable(2) { property, oldValue, newValue ->
    newValue % 2 == 0
  }

  println(noOddValuesPlease)

  noOddValuesPlease = 4
  println(noOddValuesPlease)

  noOddValuesPlease = 3
  println(noOddValuesPlease)
}

This time, the lambda expression needs to return a Boolean. If it returns true, then the property value will be set to the new value. If it returns false, though, then the property value will be left alone. Therefore, the lambda expression can “veto” a change that it does not like. In this case, we veto any change that results in an odd number, giving us:

2
4
4

Delegating Properties to Properties

Kotlin 1.4 added a new feature: allowing you to delegate a property to another property.

Off the cuff, that may sound silly.

However, it is particularly useful for dealing with API changes, especially in situations where you cannot easily change the consumers of that API, such as a library that is reused by lots of projects.

Suppose, for example, that you decide that your mis-named a public property and want to switch to a different name. Perhaps you have:

class SomethingCool {
  var oldPropertyName: String = "default"
}

…and now you want:

class SomethingCool {
  var newPropertyName: String = "default"
}

The problem is that all existing users of your API are using the old name.

You can use property delegation to “rewire” the old API to really use your new property:

class SomethingCool {
  var newPropertyName: String = "default"

  var oldPropertyName by this::newPropertyName
}

Now, anything still using oldPropertyName still works.

You can combine this with the @Deprecated annotation to steer developers towards the new name:

class SomethingCool {
  var newPropertyName: String = "default"

  @Deprecated("Please use 'newPropertyName' instead", ReplaceWith("newPropertyName"))
  var oldPropertyName by this::newPropertyName
}

Here, we scope the reference using this:: to refer to a property on the receiver itself. We could route it to some other object, if needed:

class SomethingCool(val otherCoolThing: SomethingElseCool) {
  var oldPropertyName by otherCoolThing::whatever
}

Now, references to oldPropertyName on the SomethingCool wind up actually being handled by the whatever property on a SomethingElseCool object that was supplied to the SomethingCool constructor.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.