Simplify Your Code with Kotlin Delegation
Kotlin is famous for its conciseness, but one of its most powerful features often goes underused: Delegated Properties. While most developers use the lazy delegate, Kotlin allows you to outsource the logic of getting and setting properties to a separate class. This helps eliminate boilerplate and keeps your classes focused on their primary responsibilities.
The Basics: How Delegation Works
In Kotlin, delegation uses the by keyword. When you delegate a property, you are telling the compiler that the logic for this property is handled by a specific object. The most common example is lazy, which ensures a value is only computed when it is first accessed.
val databaseConnection: Connection by lazy {
DriverManager.getConnection("jdbc:mysql://localhost/db")
}
Creating a Custom Delegate
Custom delegates are useful when you find yourself repeating the same logic across multiple properties, such as logging, validation, or local storage access. To create a custom delegate, you can implement the ReadWriteProperty or ReadOnlyProperty interface.
Let's build a delegate that automatically trims whitespace from a string every time it is assigned. This is a common requirement for user input fields.
import kotlin.reflect.KProperty
import kotlin.properties.ReadWriteProperty
class TrimmedDelegate(initialValue: String) : ReadWriteProperty<Any?, String> {
private var value = initialValue.trim()
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
this.value = value.trim()
}
}
// Usage in a class
class UserProfile {
var username: String by TrimmedDelegate("")
}
fun main() {
val profile = UserProfile()
profile.username = " KotlinDev "
println("'${profile.username}'") // Output: 'KotlinDev'
}
Why Use Delegation?
The power of delegation lies in its ability to separate concerns. Instead of cluttering your Activity or ViewModel with shared preference logic or input validation, you can encapsulate that logic in a reusable delegate class. This makes your code more modular and significantly easier to test.
Another excellent built-in delegate is Delegates.observable(). This allows you to trigger a callback every time a property changes, which is perfect for updating a UI or syncing state without writing complex setter logic.
var status: String by Delegates.observable("Idle") { prop, old, new ->
println("State changed from $old to $new")
}
Wrap up
Property delegation is more than just a syntax trick; it is a structural tool. By moving repetitive logic into delegates, you adhere to the DRY (Don't Repeat Yourself) principle and create a codebase that is easier to navigate. Next time you find yourself writing the same custom getter or setter, consider if it can be turned into a delegate.