kotlinintermediate

Map Delegation for Dynamic Properties

Delegate properties to maps: dynamic configuration, JSON-to-object mapping, and flexible data classes.

kotlin
class Config(private val map: Map<String, Any?>) {
    val host: String by map
    val port: Int by map
    val debug: Boolean by map
    val name: String by map

    override fun toString() = "Config(host=$host, port=$port, debug=$debug, name=$name)"
}

// Mutable map delegation
class MutableConfig(private val map: MutableMap<String, Any?>) {
    var host: String by map
    var port: Int by map
    var debug: Boolean by map

    fun toMap(): Map<String, Any?> = map.toMap()
    override fun toString() = map.toString()
}

// JSON-like object
class JsonObject(private val data: MutableMap<String, Any?> = mutableMapOf()) {
    var name: String? by data
    var age: Int? by data
    var email: String? by data
    var active: Boolean? by data

    fun raw() = data.toMap()
    fun has(key: String) = data.containsKey(key)

    companion object {
        fun from(vararg pairs: Pair<String, Any?>) = JsonObject(mutableMapOf(*pairs))
    }
}

// Environment-backed config
class EnvConfig {
    private val env = System.getenv().toMutableMap().apply {
        // Defaults for demo
        putIfAbsent("APP_HOST", "localhost")
        putIfAbsent("APP_PORT", "8080")
        putIfAbsent("APP_DEBUG", "false")
    }

    private val prefixed = env
        .filterKeys { it.startsWith("APP_") }
        .mapKeys { (k, _) -> k.removePrefix("APP_").lowercase() }

    val host: String by prefixed
    val port: String by prefixed
    val debug: String by prefixed
}

// Nested map delegation
class DatabaseConfig(config: Map<String, Any?>) {
    val url: String by config
    val username: String by config
    val password: String by config
    val poolSize: Int by config
}

class AppConfig(raw: Map<String, Any?>) {
    val appName: String = raw["appName"] as? String ?: "MyApp"
    val database = DatabaseConfig(
        @Suppress("UNCHECKED_CAST")
        (raw["database"] as? Map<String, Any?>) ?: emptyMap()
    )
}

fun main() {
    // Read-only config
    val config = Config(mapOf(
        "host" to "localhost",
        "port" to 8080,
        "debug" to true,
        "name" to "MyApp"
    ))
    println("Config: $config")
    println("Host: ${config.host}, Port: ${config.port}")

    // Mutable config
    val mutable = MutableConfig(mutableMapOf(
        "host" to "0.0.0.0",
        "port" to 3000,
        "debug" to false
    ))
    println("\nBefore: $mutable")
    mutable.host = "localhost"
    mutable.debug = true
    println("After: $mutable")
    println("Raw: ${mutable.toMap()}")

    // JSON-like
    val json = JsonObject.from(
        "name" to "Alice",
        "age" to 30,
        "email" to "alice@test.com"
    )
    println("\nJSON name: ${json.name}")
    json.active = true
    println("Raw: ${json.raw()}")

    // Env config
    val env = EnvConfig()
    println("\nEnv host: ${env.host}")
    println("Env port: ${env.port}")

    // Nested config
    val appConfig = AppConfig(mapOf(
        "appName" to "Production App",
        "database" to mapOf(
            "url" to "jdbc:postgresql://localhost/db",
            "username" to "admin",
            "password" to "secret",
            "poolSize" to 10
        )
    ))
    println("\nApp: ${appConfig.appName}")
    println("DB: ${appConfig.database.url} (pool: ${appConfig.database.poolSize})")
}

Use Cases

  • Dynamic configuration loading
  • JSON to typed object mapping
  • Environment variable binding

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.