
Published July 01, 2019
Mastering Kotlin: Property Delegates
Kotlin Clean-Code Android Design-Patterns
Advanced Kotlin - Delegates
This post is part of an on-going series discussing some of the more advanced and intermediate features of the Kotlin programming language.
The target audience for this series of posts will be Kotlin developers who are comfortable with the language but would like to learn about the more advanced features that are available. Some of the items I’ll cover over the coming months will be DSLs, reflection, tail recursion, use-site targets, and much more. If this resonates with you, read on!
To kick off the series, we’ll look at delegates in Kotlin. I’ll discuss both the built-in delegates provided by the standard library and also dive into how (and when) to create your own.
Delegates?
But first, what exactly is a property delegate? I guarantee you’ve used a delegate before if you’ve been writing Kotlin for any length of time:
val myString by lazy { "Some Value" }
Here lazy
is a property delegate that Kotlin provides us. It’s one of the four built-in delegates from the stdlib,
and probably the most used and most well-known. We’ll take a look at the others in a moment.
A delegate is simply a way to wrap common code that will be executed each time you read and/or write from a
property value. In this case lazy
is used only to construct the value of a field when it is first
accessed (aka “lazy loading”).
In the case of lazy
this is a top-level Kotlin function that returns an instance of Lazy, which
conforms to a very specific interface that allows it to be treated as a delegate by the Kotlin compiler. Each time
myString
is referenced the code within the stdlib implementation of Lazy
gets invoked. It’s simply a means of
code-sharing that helps you adhere to the DRY (Don’t Repeat Yourself) good coding practice.
There’s more than just lazy
?
Yup. The stdlib actually provides 3 other delegates that are useful to know about.
observable
vetoable
notNull
Let’s look at each of these in turn.
The observable
delegate
The next stdlib delegate we’ll look at is observable
(not to be confused with
RxJava’s Observable
). Like the other built-in delegates, this is found in Kotlin’s Delegates
class.
Usage is pretty straight-forward:
var maxCount: Int by observable(initialValue = 0) { property, oldValue, newValue ->
println("${property.name} is being changed from $oldValue to $newValue")
}
This delegate takes two parameters; an initial value and a lambda. The lambda will be executed each time the value of the field is changed.
fun main() {
maxCount++
maxCount++
maxCount += 10
}
> maxCount is being changed from 0 to 1
> maxCount is being changed from 1 to 2
> maxCount is being changed from 2 to 12
This delegate is great if you need to drive one property off of another, log changes (like in the example above), cache old values for undo-style actions, and various other things.
The vetoable
delegate
The signature of vetoable
looks very much like observable
:
var age: Int by vetoable(initialValue = 0) { property, oldValue, newValue ->
newValue > 0
}
The difference between observable
and vetoable
is that the lambda parameter for vetoable
must return a Boolean
.
If false
is returned then the value will not be modified.
This delegate is a great way to either set reasonable bounds on a value or implement a simple validation framework.
The notNull
delegate
notNull
is the simplest of the four stdlib delegates. It works similar to lateinit
in that it will throw an
IllegalStateException
if a variable is accessed before it is initialized.
var age by notNull<Int>()
fun main() = println(age)
> Exception in thread "main" java.lang.IllegalStateException: Property age should be initialized before get.
This brings up a good question; what is the difference between notNull
and lateinit
? When would you have
to use notNull
when lateinit
does the same thing? The answer is that lateinit
can only be used on
non-primitive types. If you tried to change the above example to “lateinit var age: Int
” you would receive the
compilation error “‘lateinit’ modifier is not allowed on properties of primitive types”. This is exactly
what notNull
is meant to solve.
Creating your own delegates
Creating your own delegates is a matter of creating a class that has a specific getValue
function for
delegates used by val
properties, and also a specific setValue
function if you want your delegate
to also be usable for var
s.
The signatures of these functions are as follows:
fun getValue(thisRef: R, property: KProperty<*>): T
fun setValue(thisRef: R, property: KProperty<*>, value: T)
Since the creation of delegates is a compiler feature (vs a runtime feature) you actually don’t need to
implement an interface to provide these functions. As long as a class implements these with the operator
keyword, the compiler will treat your class as a delegate. However, the stdlib does provide a few interfaces
that can be used regardless of this fact. I highly suggest using these interfaces so that future developers
looking at your code know that a class is indeed a delegate and the interface also provides a reference for docs.
Using the stdlib delegate interfaces also makes it impossible to get the function signatures wrong.
Here are the two interfaces and one abstract class we’ll look at:
The ReadOnlyProperty
interface
The ReadOnlyProperty
interface only has the getValue()
function, so delegates created with it can only be used
on val
properties.
Let’s create a simple delegate using the ReadOnlyProperty
interface and see how it works.
class SimpleReadOnlyDelegate<out T>(private val id: T) : ReadOnlyProperty<Any, T> {
override fun getValue(thisRef: Any, property: KProperty<*>) = id.also {
println("Getting value for ${property.name} from ${thisRef::class.simpleName}")
}
}
class DelegateExample {
val myProperty by SimpleReadOnlyDelegate("demo")
}
fun main() = println(DelegateExample().myProperty)
> Getting value for myProperty from DelegateExample
> "demo"
If you tried to use our SimpleReadOnlyDelegate
delegate on a var
type the compiler lets you know that
the setValue
method is missing:
> Missing 'setValue(DelegateExample, KProperty<*>, String)' method on delegate of type 'SimpleReadOnlyDelegate'
We can fix this by changing our class from implementing ReadOnlyProperty
to instead implement ReadWriteProperty
which contains both getValue
and setValue
functions.
The ReadWriteProperty
interface
If we want to allow both reading and writing values from a delegate we must also include a setValue
function.
Here’s a very simple delegate that shows using the ReadWriteProperty
interface. Note that for brevity this
delegate does nothing more than store and return a value. We’ll take a look at some more complex examples
further down. For now let’s focus on the basic structure.
class SimpleDelegate<T> : ReadWriteProperty<Any, T?> {
private var value: T? = null
override fun getValue(thisRef: Any, property: KProperty<*>) = value
override fun setValue(thisRef: Any, property: KProperty<*>, value: T?) {
this.value = value
}
}
class DelegateDemo {
var someNum by SimpleDelegate<Int>()
var someStr by SimpleDelegate<String>()
}
fun main() {
val demo = DelegateDemo().apply {
someNum = 42
someStr = "demo"
}
println("${demo.someNum} ${demo.someStr}")
}
> 42 demo
Now that you understand how delegates work and how they are created, you can hopefully see the power that they can provide in helping to encapsulate reusable code.
A few complex examples would include a delegate that stores and retrieves values from a database
“var record by MyDBDelegate<String>()
”, or on Android a delegate that backs values using SharedPreferences
(local storage) “var pref by MySharedPrefsDelegate()
”, or a delegate similar to lazy
which constructs parts
of an object that is expensive to initialize.
The ObservableProperty
abstract class
The last item that I’ll cover is an abstract class in the stdlib called ObservableProperty
.
If you take a peak at the source code for this class you’ll find that it implements logic that allows you to
validate properties in a beforeChange
function which is called from setValue
. If beforeChange
returns false
then the property value is not updated. It also provides a (default no-op) afterChange
function which is called
after setValue
is executed.
public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
private var value = initialValue
/**
* If the callback returns `true` the value of the property is being set to the new value,
* and if the callback returns `false` the new value is discarded.
*/
protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true
protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val oldValue = this.value
if (!beforeChange(property, oldValue, value)) { return }
this.value = value
afterChange(property, oldValue, value)
}
public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value
}
}
Does the ability to block property value modification like this sound familiar?
You guessed it; ObservableProperty
is exactly how the vetoable
delegate that we saw before is implemented
in the stdlib…
public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
}
If you find vetoable
too restrictive or find that you need to share the logic in a vetoable
block accross multiple
properties then you should be looking at extending ObservableProperty
to create your own delegate instead of using vetoable
.
Adding One More Layer
Sometimes you need an extra layer of logic between your property and the creation of a delegate.
Kotlin provides a means of wrapping the creation of a delegate via an operator called provideDelegate
.
This allows you to continue to use the by
keyword when defining your property, but the right side of the by
will
reference a class that returns a delegate, but is not a delegate itself.
class MyDelegateFactory {
operator fun provideDelegate(thisRef: Any, prop: KProperty<*>): ReadWriteProperty<Any, String> {
println("returning a new ReadWriteProperty delegate for ${prop.name}")
return MyDelegate()
}
}
class MyDelegate: ReadWriteProperty<Any, String> {
override fun getValue(thisRef: Any, property: KProperty<*>): String { ... }
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { ... }
}
class DelegateDemo {
var myProp by MyDelegateFactory()
}
Another common pattern is to hide the delegate creation behind a top-level function, like we saw with lazy
, observable
, etc.
Stay tuned! More to come!
Hopefully this article provided enough information on Kotlin delegates for you to get started on building
your own, and gave you a few additional items in your tool chest for using observable
, vetoable
, and notNull
.
If you would like to read more about delegates in Kotlin take a look at:
- The Kotlin properties package
- The official Delegated Properties docs
- Chapter 7 in Kotlin In Action
Again, this is the first in a series of “Advanced Kotlin” articles on the American Express Technology Blog. Stay tuned for more!
Happy delegating.
About the Author

Recent Articles

Brent Watson
Staff Engineer
Mastering Kotlin: Use-Site Targets
A deep-dive on use-site targets in Kotlin.