skip to content

Custom ReadWriteProperty Delegates

You write a delegate by implementing ReadOnlyProperty or ReadWriteProperty, or just by supplying operator getValue and setValue. Map-backed delegation, where a property reads its own name as a key, is the classic demonstration.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is the exact operator function signature a class must provide so an instance of it can be used as a custom delegate for a read-only `val` property via `by`?

level: juniorimportance: must knowfreq 55%

answer

  1. operator fun getValue(thisRef, property: KProperty<*>): T
  2. var adds setValue(thisRef, property, value)
  3. thisRef = owner, null for top-level
  4. property.name from KProperty
  5. ReadOnlyProperty / ReadWriteProperty interfaces

basics

~10 s

The delegate needs an operator function named getValue that takes the owner object and the property as parameters and returns the value. For a var you also add a setValue.

solid answer

~40 s

For a read-only `val x by delegate`, the delegate type must declare `operator fun getValue(thisRef: R, property: KProperty<*>): T`, where `R` is the owner's type (or `Any?`/a supertype) and `T` is the property type. `thisRef` is the object that owns the property (or `null` for top-level), and `property` is a `KProperty<*>` reflection handle (so you can read `property.name`). The `operator` keyword is mandatory; the compiler rewrites `x` into `delegate.getValue(this, ::x)`. For a `var`, add `operator fun setValue(thisRef: R, property: KProperty<*>, value: T)`. You can either implement these directly or implement the stdlib interface `ReadOnlyProperty<R, T>` / `ReadWriteProperty<R, T>`, which declare exactly these operator functions.

code

kotlin · 11 lines
kotlin
import kotlin.reflect.KProperty

class TrimDelegate {
    private var value = ""
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String = value
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        this.value = value.trim()
    }
}

class Form { var input: String by TrimDelegate() }

go deeper

for a junior

States the getValue signature and that var adds setValue; knows operator is required.

for a middle

Explains thisRef vs property, that the convention is structural, and that ReadOnlyProperty/ReadWriteProperty are the stdlib helpers.

for a senior

Discusses the compiler rewrite to getValue(this, ::prop), reuse via Any? thisRef, and when to implement directly vs. via interface.

for a principal

Frames delegation as a structural convention with reflection metadata, weighs API ergonomics (variance in R, out T), and reflective overhead implications.

## What a delegated property is When you write `val name: String by myDelegate`, Kotlin does NOT store the value in a backing field. Instead it stores `myDelegate` and routes every read/write of `name` through operator functions on that delegate object. This is the *delegated property* convention. ## The getValue convention For a read-only `val`, the delegate must provide: ```kotlin operator fun getValue(thisRef: R, property: KProperty<*>): T ``` - `operator` — REQUIRED keyword; it marks the function as part of a Kotlin convention so the compiler will call it implicitly. - `thisRef` — the object that *owns* the property. For a member property it's the enclosing instance; for a top-level property it's `null` (so the type is often `Any?`). Declaring it as a supertype like `Any?` makes the delegate reusable. - `property: KProperty<*>` — a reflection handle to the property being delegated. `KProperty` comes from `kotlin.reflect`. The most common use is `property.name` (the property's source name) for map keys, logging, etc. The `<*>` star projection means "any property type." - return `T` — must be assignable to the property's declared type. The compiler rewrites the read of `name` as `myDelegate.getValue(this, this::name)`. ## var: add setValue For a mutable `var`, the delegate must ALSO declare: ```kotlin operator fun setValue(thisRef: R, property: KProperty<*>, value: T) ``` A write `name = "x"` becomes `myDelegate.setValue(this, this::name, "x")`. ## Two ways to satisfy the convention 1. **Implement the operator functions directly** on any class. No interface needed — the convention is structural. 2. **Implement a stdlib interface**: `ReadOnlyProperty<in R, out T>` (declares `getValue`) or `ReadWriteProperty<in R, T>` (declares `getValue` + `setValue`). These already mark the functions `operator`, so you just override them. ```kotlin class UppercaseDelegate : ReadWriteProperty<Any?, String> { private var stored = "" override fun getValue(thisRef: Any?, property: KProperty<*>): String = stored override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { stored = value.uppercase() } } class User { var name: String by UppercaseDelegate() } ``` ## Key points - The convention is matched by *signature*, not by implementing a specific interface. - `thisRef` lets the delegate behave differently depending on the owner; `property` exposes metadata like `property.name`. - A local `val` delegated property is allowed too; `thisRef` is then `null`.

  • Why is the `operator` keyword required on getValue/setValue?
    Because delegated properties are a compiler convention; `operator` tells the compiler these functions may be invoked implicitly. Without it the compiler will not recognize the type as a valid delegate and `by` won't compile.
  • What does the `property` parameter give you access to?
    A `KProperty<*>` reflection object — most usefully `property.name`, but also annotations, return type, and visibility metadata about the delegated property.

The delegate is a property's hired manager: every time someone reads or writes the property, the manager (getValue/setValue) is called instead of touching a field directly.

saying these in an interview costs you the question

  • Forgetting the `operator` keyword on getValue/setValue
  • Saying the value is stored in the property's backing field (it isn't; the delegate holds it)
  • Omitting the `property: KProperty<*>` parameter
  • Claiming you MUST implement ReadWriteProperty (the convention is structural, not nominal)
  • Confusing setValue's parameter order (thisRef, property, value)

context

open as a page

Explain map-backed delegation: how does `val name: String by map` work, what key is used, and how does delegating to a `MutableMap` differ?

level: middleimportance: should knowfreq 42%

basics

~20 s

A property can delegate to a map; reading the property looks it up in the map by the property's own name. With a mutable map, assigning the property writes that key back into the map.

open as a page

Compare implementing a custom delegate by overriding `ReadWriteProperty<R, T>` versus declaring `operator fun getValue/setValue` directly. When would you pick each, and what do the interface's type parameters mean?

level: middleimportance: should knowfreq 45%

basics

~10 s

Both work. The interface gives you a named, reusable type with the right method shapes already declared. Writing the operator functions directly is lighter when you don't need a shared interface type.

open as a page

How can a custom delegate use its `thisRef` and `property: KProperty<*>` parameters? Give a concrete delegate whose behaviour depends on the owner and on the property's name.

level: seniorimportance: should knowfreq 33%

basics

~10 s

thisRef is the object that owns the property, so the delegate can read or notify it. property gives the property's name and metadata, useful for keys, logging, or validation messages.

open as a page

A junior writes a reusable `var`-delegate that stores its value in a single field on the delegate instance, then uses one delegate object across several properties. What breaks, and how do you design a correct reusable custom delegate?

level: seniorimportance: should knowfreq 28%

basics

~20 s

If one delegate object holds a single value field and is shared by several properties, they all read and write the same field, so the properties overwrite each other. Either give each property its own delegate, or key the storage by the property.

open as a page