Question
While developing for Android, I sometimes see Kotlin code like this:
var someModel: SomeViewModel by notNullAndObservable { vm ->
// ...
}
I do not understand what the by keyword means here or what role it plays in this property declaration. How does by work in Kotlin, and what is it doing in examples like this?
Short Answer
By the end of this page, you will understand how Kotlin uses the by keyword for property delegation, why it appears in Android and Kotlin codebases, and how it changes the way a property stores or computes its value. You will also see how delegated properties differ from normal properties, how they work step by step, and when developers use them in real projects.
Concept
In Kotlin, the by keyword often means delegation.
In your example:
var someModel: SomeViewModel by notNullAndObservable { vm ->
// ...
}
someModel is not managing its value entirely by itself. Instead, Kotlin delegates the work of getting and setting the property to another object returned by notNullAndObservable(...).
What is a delegated property?
A delegated property is a property whose behavior is handled by a delegate object.
Instead of writing your own custom getter and setter like this:
var someModel: SomeViewModel = ...
get() = field
set(value) {
field = value
// extra logic
}
You can write:
var someModel: SomeViewModel by someDelegate
Kotlin then translates property access behind the scenes so that:
- reading
someModelcalls the delegate'sgetValue(...) - writing
someModel = ...calls the delegate'ssetValue(...)
Why this matters
Delegated properties are useful when a property needs extra behavior, such as:
- lazy loading
- validation
- observing changes
- storing values in maps
- enforcing non-null initialization later
- connecting a property to shared preferences, state holders, or framework APIs
In Android development, this is common because many values are not available immediately at object creation time, or because developers want code to react automatically when a property changes.
What by means in your example
The name notNullAndObservable strongly suggests a custom delegate that combines two behaviors:
- the property must not be read before it is initialized
- when the property changes, a callback runs
So this line likely means:
someModelstarts without a usable value- if you access it too early, the delegate may throw an error
- once you assign a value, the delegate stores it
- when the value changes, the lambda
{ vm -> ... }is called
The exact behavior depends on how notNullAndObservable is implemented, but the role of by is to tell Kotlin: use that delegate object to control this property.
Mental Model
Think of a normal property as a box that stores its own value.
A delegated property is more like a front desk clerk.
When you try to read or write the property, you are not opening the box directly. You are asking the clerk to handle the request.
property = value→ the clerk decides how to store itprintln(property)→ the clerk decides how to return it- extra rules can happen in between, like logging, validation, or refusing access before setup
So by is Kotlin's way of saying:
"This property is handled by someone else."
That "someone else" is the delegate object.
Syntax and Examples
Basic delegated property syntax
var name: String by delegateObject
For a read-only property:
val name: String by delegateObject
The delegate object must provide operator functions such as:
operator fun getValue(thisRef: Any?, property: KProperty<*>): String
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String)
val only needs getValue. var needs both getValue and setValue.
Example 1: Using Kotlin's built-in lazy
val message: String by lazy {
println("Computing message")
"Hello"
}
fun main() {
println(message)
println(message)
}
What happens?
messageis not computed immediately- the first time you read it, the lambda runs
- after that, Kotlin remembers the result
Output:
Computing message
Hello
Hello
Example 2: A custom observable delegate
Kotlin provides a built-in delegate for observing property changes:
import kotlin.properties.Delegates
var username: String by Delegates.observable("") { property, oldValue, newValue ->
println("${property.name}: $oldValue -> $newValue")
}
fun main() {
username = "alice"
username = "bob"
}
Output:
username: -> alice
username: alice -> bob
Example 3: Similar idea to your Android example
import kotlin.properties.Delegates
var model: String by Delegates.observable("initial") { _, oldValue, newValue ->
println("Model changed from $oldValue to $newValue")
}
This means the property is still used like a normal variable, but the delegate adds extra behavior each time it changes.
Step by Step Execution
Consider this example:
import kotlin.properties.Delegates
var status: String by Delegates.observable("offline") { _, oldValue, newValue ->
println("Status changed from $oldValue to $newValue")
}
fun main() {
println(status)
status = "online"
println(status)
}
Step-by-step
1. Property declaration
var status: String by Delegates.observable("offline") { ... }
Kotlin creates a delegate object using Delegates.observable(...).
That delegate stores the current value, starting with "offline".
2. First read
println(status)
This does not read a normal backing field directly.
Instead, Kotlin calls the delegate's getValue(...) function.
The delegate returns "offline".
Output:
offline
3. Write a new value
status = "online"
Kotlin calls the delegate's setValue(...) function.
The delegate:
- sees the old value:
"offline" - stores the new value:
"online" - runs the callback
Output:
Status changed from offline to online
4. Second read
println(status)
Again, Kotlin calls getValue(...) on the delegate.
Now the stored value is "online", so that is what gets printed.
Output:
online
Mental trace
You can think of Kotlin rewriting the property access roughly like this:
val delegate = Delegates.observable("offline") { _, oldValue, newValue ->
println("Status changed from $oldValue to $newValue")
}
println(delegate.getValue(...))
delegate.setValue(..., "online")
println(delegate.getValue(...))
The actual generated code is more detailed, but this is the core idea.
Real World Use Cases
Delegated properties are used in many practical Kotlin situations.
1. Lazy initialization
val config by lazy { loadConfigFromDisk() }
Use this when an object is expensive to create and should only be built if needed.
2. Observing state changes
var score by Delegates.observable(0) { _, old, new ->
println("Score changed: $old -> $new")
}
Useful for UI state, settings, or debugging value changes.
3. Preventing use before initialization
import kotlin.properties.Delegates
var service: String by Delegates.notNull()
Useful when a property cannot be assigned in the constructor but must be set before use.
4. Validation before accepting a value
import kotlin.properties.Delegates
var age: Int by Delegates.vetoable(0) { _, _, newValue ->
newValue >= 0
}
The delegate can reject invalid updates.
5. Android and UI frameworks
Delegation is common in Android and modern Kotlin UI code for:
- view binding helpers
- fragment arguments
- state properties
- preference-backed values
- lifecycle-aware accessors
6. Map-backed properties
class User(private val data: Map<String, Any>) {
val name: String by data
val age: Int by data
}
This is useful when reading JSON-like data, configuration, or dynamic records.
Real Codebase Usage
In real projects, developers use delegated properties to reduce boilerplate and centralize repeated logic.
Common patterns
Guarding access until initialization
A service or view model may not be available immediately:
import kotlin.properties.Delegates
class Controller {
var repository: String by Delegates.notNull()
}
This avoids nullable types when the value should definitely exist before real use.
Reacting to changes automatically
import kotlin.properties.Delegates
class Settings {
var theme: String by Delegates.observable("light") { _, old, new ->
if (old != new) println("Theme updated")
}
}
This is common for syncing state, updating UI, logging, or triggering side effects.
Encapsulating repeated property rules
Teams often create custom delegates to avoid repeating getter/setter logic.
For example, a custom delegate might:
- trim strings automatically
- save values to preferences
- validate input
- log every change
- connect properties to a framework object
Cleaner APIs
Instead of this:
private var _token: String? = null
var token: String
get() = _token ?: error("Token not initialized")
set(value) {
_token = value
println("Token updated")
}
A delegate can package that behavior into reusable logic.
Why teams like this approach
- less repeated code
- more readable property declarations
- reusable behavior across many classes
- fewer manual getter/setter mistakes
- clear intent at the declaration site
Common Mistakes
1. Thinking by always means inheritance or copying
It does not copy a value and it does not mean subclassing.
In property declarations, by means the property is delegated.
2. Assuming the property has a normal backing field
A delegated property may not use a regular backing field in the way a normal property does. The delegate controls storage and behavior.
3. Forgetting that var needs both get and set support
Broken idea:
class ReadOnlyDelegate {
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): String {
return "hello"
}
}
var name: String by ReadOnlyDelegate()
This fails because var requires setValue(...) too.
Fix:
- use
val, or - implement
setValue(...)
4. Reading Delegates.notNull() before assigning
Broken code:
import kotlin.properties.Delegates
var token: String by Delegates.notNull()
fun main() {
println(token)
}
This throws an exception because the property was never initialized.
Fix:
import kotlin.properties.Delegates
var token: String by Delegates.notNull()
fun main() {
token = "abc123"
println(token)
}
5. Putting too many side effects into delegates
A delegate should make property behavior clearer, not surprising.
Avoid delegates that silently:
- make network calls on simple reads
- write to disk unexpectedly
- trigger large chains of hidden logic
6. Confusing delegated properties with by used elsewhere
Kotlin also uses by in interface delegation, for example:
class MyList<T>(private val inner: MutableList<T>) : MutableList<T> by inner
That is related to delegation in general, but it is not the same as a delegated property.
Comparisons
Delegated property vs normal property
| Feature | Normal property | Delegated property |
|---|---|---|
| Storage | Usually stored directly in the class | Often stored/managed by another object |
| Custom behavior | Manual getter/setter | Reusable delegate logic |
| Boilerplate | Can grow quickly | Often shorter |
| Read/write interception | You write it yourself | Delegate handles it |
by vs = in property declarations
| Syntax | Meaning |
|---|---|
var x = 10 | Store the value directly in the property |
var x by delegate | Let another object control reads/writes |
Common built-in delegates
| Delegate | Purpose |
|---|---|
lazy | Compute value only when first needed |
Delegates.observable | Run code after value changes |
Delegates.vetoable | Accept or reject new values |
Delegates.notNull | Require assignment before first read |
Delegated properties vs custom getters/setters
| Approach | Best when |
|---|---|
| Custom getter/setter | Logic is specific to one property |
| Delegate | Logic should be reusable across many properties |
Property delegation vs interface delegation
Both use by, but they apply to different things.
| Kind | Example | Purpose |
|---|---|---|
| Property delegation | var x by delegate | Delegate property access |
| Interface delegation | class A(b: B) : B by b | Forward interface implementation to another object |
Cheat Sheet
Quick syntax
val name: Type by delegate
var name: Type by delegate
What by means
- Delegate property behavior to another object
- Reads call
getValue(...) - Writes call
setValue(...)forvar
Delegate requirements
For val
operator fun getValue(thisRef: Any?, property: KProperty<*>): Type
For var
operator fun getValue(thisRef: Any?, property: KProperty<*>): Type
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Type)
Built-in delegates
val config by lazy { loadConfig() }
var name by Delegates.observable("") { _, old, new ->
println("$old -> $new")
}
var token: String by Delegates.notNull()
var age by Delegates.vetoable(0) { _, _, new -> new >= 0 }
Key rule
=assigns a value directlybydelegates the property to another object
Common edge cases
Delegates.notNull()throws if read before assignmentvardelegates must supportsetValue(...)lazyis forval, not mutablevarbycan also be used for interface delegation, which is a different feature
FAQ
What does the by keyword mean in Kotlin?
In many contexts, by means delegation. For properties, it means another object handles getting and setting the property value.
Is by the same as assigning a value?
No. = stores a value directly. by connects the property to a delegate object that controls access.
Why is by common in Android Kotlin code?
Android code often needs lazy setup, state observation, and delayed initialization. Delegated properties make those patterns shorter and cleaner.
What is a delegated property in Kotlin?
A delegated property is a property whose behavior is implemented by another object through getValue and optionally setValue.
What happens when I use Delegates.notNull()?
The property must be assigned before you read it. If you read it too early, Kotlin throws an exception.
Can I create my own delegate?
Yes. You can write a class with operator fun getValue(...) and operator fun setValue(...) and use it with by.
Is by only for properties?
No. Kotlin also uses by for interface delegation, but that is a separate use case from delegated properties.
When should I use a delegate instead of a custom setter?
Use a delegate when the behavior is reusable or when you want cleaner property declarations. Use a custom setter when the logic is small and specific to one property.
Mini Project
Description
Build a small Kotlin class that tracks an app setting using delegated properties. This project demonstrates how by can add behavior to a property without writing manual getters and setters every time. You will use an observable delegate so changes are logged automatically.
Goal
Create a settings class where property changes are observed and printed whenever the value is updated.
Requirements
- Create a Kotlin class named
AppSettings. - Add a mutable property named
themeusingby Delegates.observable. - Start the property with the value
"light". - Print the old and new values whenever
themechanges. - In
main, create anAppSettingsobject and change the theme at least twice.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.