简体   繁体   中英

Kotlin - How to make a property delegate by map with a custom name?

I'm trying to get my head around property delegates, and I have an interesting use case. Is it possible to have something like this:

class MyClass {
    val properties = mutableMapOf<String, Any>()
    val fontSize: Any by MapDelegate(properties, "font-size")
}

That would allow me to store fontSize using the map as a delegate, but with a custom key (ie "font-size").

The specific use case if for storing things like CSS property tags that can be accessed through variables ( fontSize ) for use in code, but can be rendered properly when iterating through the map ( font-size: 18px; ).

The documentation on the delegated properties is a good source of information on the topic. It probably is a bit longer read than the following examples:

fun <T, TValue> T.map(properties: MutableMap<String, TValue>, key: String): ReadOnlyProperty<T, TValue> {
    return object : ReadOnlyProperty<T, TValue> {
        override fun getValue(thisRef: T, property: KProperty<*>) = properties[key]!!
    }
}

class MyClass {
    val properties = mutableMapOf<String, Any>()
    val fontSize: Any by map(properties, "font-size")
}

You can ease up things a little bit and avoid typing the CSS property name by converting Kotlin property names to CSS attributes equivalents like so:

fun <T, TValue> map(properties: Map<String, TValue>, naming:(String)->String): ReadOnlyProperty<T, TValue?> {
    return object : ReadOnlyProperty<T, TValue?> {
        override fun getValue(thisRef: T, property: KProperty<*>) = properties[naming(property.name)]
    }
}

object CamelToHyphen : (String)->String {
    override fun invoke(camelCase: String): String {
        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, camelCase)
    }
}

fun <T, TValue> T.cssProperties(properties: Map<String,TValue>) = map(properties, CamelToHyphen)

class MyClass {
    val properties = mutableMapOf<String, Any>()
    val fontSize: Any? by cssProperties(properties)
}

The above sample uses Guava's CaseFormat .

If you'd like to have mutable property your delegate will have to implement setter method:

fun <T, TValue> map(properties: MutableMap<String, TValue?>, naming: (String) -> String): ReadWriteProperty<T, TValue?> {
    return object : ReadWriteProperty<T, TValue?> {
        override fun setValue(thisRef: T, property: KProperty<*>, value: TValue?) {
            properties[naming(property.name)] = value
        }

        override fun getValue(thisRef: T, property: KProperty<*>) = properties[naming(property.name)]
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM