Kotlin Academy · Lesson

Delegates for Preferences, Map-Backed Properties & Logging

Apply delegates to SharedPreferences, Map storage, and audit logging.

Lesson 4 of 413 steps

Delegates for Preferences, Map-Backed Properties & Logging is a free Kotlin Academy lesson on CoddyKit. This is lesson 4 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, and your progress syncs across the web and the CoddyKit app. The Kotlin Academy course includes 4 lessons in total.

Map-Backed Properties

Kotlin allows delegating properties to a Map directly. Reading the property looks up its name in the map.

class User(map: Map<String, Any?>) {
    val name: String by map
    val age: Int by map
}
fun main() {
    val u = User(mapOf("name" to "Alice", "age" to 30))
    println(u.name) // Alice
    println(u.age)  // 30
}

MutableMap Delegate

For mutable properties, delegate to a MutableMap and mutations update the underlying map.

class Settings(private val map: MutableMap<String, Any?> = mutableMapOf()) {
    var theme: String by map
    var fontSize: Int by map
}
fun main() {
    val s = Settings()
    s.theme = "dark"
    s.fontSize = 16
    println(s.theme)    // dark
    println(s.fontSize) // 16
}

JSON Deserialization with Map Delegate

Parse a JSON-like map and expose its fields as typed properties using map delegation — great for dynamic data.

class ApiResponse(data: Map<String, Any?>) {
    val id: Int by data
    val title: String by data
    val published: Boolean by data
}
fun main() {
    val response = ApiResponse(mapOf("id" to 1, "title" to "Kotlin Rocks", "published" to true))
    println(response.title)     // Kotlin Rocks
    println(response.published) // true
}

Logging Delegate Pattern

Wrap any delegate with logging to trace every read and write transparently across all delegated properties.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class Logged<T>(private var v: T) : ReadWriteProperty<Any?, T> {
    override fun getValue(t: Any?, p: KProperty<*>): T {
        println("[READ]  ${p.name} = $v"); return v
    }
    override fun setValue(t: Any?, p: KProperty<*>, value: T) {
        println("[WRITE] ${p.name}: $v -> $value"); v = value
    }
}
var config: String by Logged("default")
fun main() { println(config); config = "production" }

Combining Map + Logging

Layer a logging wrapper around map delegation for full auditability of dynamic property access.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class TrackedMap(private val map: MutableMap<String,Any?> = mutableMapOf()) {
    inner class Delegate<T> : ReadWriteProperty<Any?,T> {
        @Suppress("UNCHECKED_CAST")
        override fun getValue(t:Any?,p:KProperty<*>): T {
            val v = map[p.name] as T; println("read ${p.name}=$v"); return v
        }
        override fun setValue(t:Any?,p:KProperty<*>,value:T) {
            println("write ${p.name}=$value"); map[p.name]=value
        }
    }
    var host: String by Delegate()
}
fun main() { val m=TrackedMap(); m.host="localhost"; println(m.host) }

SharedPreferences-Style Delegate

A delegate that stores values in a backing map simulating SharedPreferences — adapt to real Android prefs by swapping the map for actual prefs calls.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val prefs = mutableMapOf<String, Any?>()
fun <T> pref(default: T): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t: Any?, p: KProperty<*>): T = prefs.getOrDefault(p.name, default) as T
    override fun setValue(t: Any?, p: KProperty<*>, value: T) { prefs[p.name] = value }
}
object UserPrefs {
    var username: String by pref("Guest")
    var isDarkMode: Boolean by pref(false)
}
fun main() {
    println(UserPrefs.username)   // Guest
    UserPrefs.username = "Alice"
    println(UserPrefs.username)   // Alice
}

Typed Preference Helpers

Create typed helper functions for each primitive preference type for cleaner call sites.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val store = mutableMapOf<String,Any?>()
fun stringPref(default: String) = object : ReadWriteProperty<Any?,String> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t:Any?,p:KProperty<*>) = store.getOrDefault(p.name, default) as String
    override fun setValue(t:Any?,p:KProperty<*>,v:String) { store[p.name]=v }
}
fun intPref(default: Int) = object : ReadWriteProperty<Any?,Int> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t:Any?,p:KProperty<*>) = store.getOrDefault(p.name, default) as Int
    override fun setValue(t:Any?,p:KProperty<*>,v:Int) { store[p.name]=v }
}
object Prefs {
    var name: String by stringPref("default")
    var count: Int by intPref(0)
}
fun main() { Prefs.name="Bob"; Prefs.count=5; println("${Prefs.name}: ${Prefs.count}") }

Audit Log Delegate

Record every property mutation with timestamp to an audit list — ideal for compliance or debugging scenarios.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val audit = mutableListOf<String>()
class AuditDelegate<T>(private var v: T) : ReadWriteProperty<Any?, T> {
    override fun getValue(t:Any?,p:KProperty<*>) = v
    override fun setValue(t:Any?,p:KProperty<*>,value:T) {
        audit += "[${System.currentTimeMillis()}] ${p.name}: $v -> $value"
        v = value
    }
}
var price: Double by AuditDelegate(9.99)
fun main() {
    price = 12.99
    price = 11.49
    audit.forEach(::println)
}

Validation + Map Storage

Combine map storage with validation: reject invalid values before writing to the map.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val data = mutableMapOf<String, Any?>()
fun positiveInt(default: Int): ReadWriteProperty<Any?,Int> = object : ReadWriteProperty<Any?,Int> {
    @Suppress("UNCHECKED_CAST")
    override fun getValue(t:Any?,p:KProperty<*>)=data.getOrDefault(p.name,default) as Int
    override fun setValue(t:Any?,p:KProperty<*>,v:Int){
        require(v>=0){"${p.name} must be >= 0"}
        data[p.name]=v
    }
}
object Inventory { var stock: Int by positiveInt(100) }
fun main(){
    Inventory.stock=50; println(Inventory.stock)
    try { Inventory.stock=-1 } catch(e:Exception){ println(e.message) }
}

Delegate Composition Pattern

Compose multiple delegate behaviors using a builder-style API: start with a base delegate and layer on logging, validation, or caching.

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
fun <T> ReadWriteProperty<Any?,T>.withLogging(): ReadWriteProperty<Any?,T> {
    val base = this
    return object : ReadWriteProperty<Any?,T> {
        override fun getValue(t:Any?,p:KProperty<*>): T {
            val v = base.getValue(t,p); println("get ${p.name}=$v"); return v
        }
        override fun setValue(t:Any?,p:KProperty<*>,v:T){
            println("set ${p.name}=$v"); base.setValue(t,p,v)
        }
    }
}

Real-World: Config Object Pattern

Use map delegation to build a configuration object from external sources like environment variables or a JSON config file.

class AppConfig(private val raw: Map<String, Any?>) {
    val host: String by raw
    val port: Int by raw
    val debug: Boolean by raw
}
fun main() {
    val env = mapOf("host" to "0.0.0.0", "port" to 8080, "debug" to true)
    val cfg = AppConfig(env)
    println("${cfg.host}:${cfg.port} debug=${cfg.debug}")
}

Quick Check

What happens when you delegate a property to a Map?

Recap

Delegates unlock powerful patterns: map delegation for dynamic/JSON data, SharedPreferences-style persistence, audit logging for compliance, and composable validation — all without changing call sites.

Free to start

Learn Kotlin with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
51
Lessons
203

Frequently Asked Questions

Is the “Delegates for Preferences, Map-Backed Properties & Logging” lesson free?

Yes — any 3 lessons of the Kotlin Academy learning path, including “Delegates for Preferences, Map-Backed Properties & Logging”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Kotlin Academy course includes 4 lessons in total.

What will I learn in “Delegates for Preferences, Map-Backed Properties & Logging”?

Apply delegates to SharedPreferences, Map storage, and audit logging. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 4 of 4.

How long does the “Delegates for Preferences, Map-Backed Properties & Logging” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. by lazy: Deferred Initialization Deep Dive
  2. observable and vetoable Delegates
  3. Writing a Custom ReadWriteProperty Delegate
  4. Delegates for Preferences, Map-Backed Properties & Logging
← Back to Kotlin Academy