Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Property delegation baked by mutable map

Tags:

kotlin

I have a following code:

class Mapped(var data:Map<String,String>){
    val firstName:String by data
}

This works fine in case the Mapped is used as follows:

val mapped = Mapped(mapOf("firstName" to "initialFirstName"))
println(mapped.firstName); // prints "initialFirstName"

However since the data property is mutable we can change it's value i.e.:

mapped.data = mapOf("firstName" to "updated");

However the firstName property still holds the "initialFirstName".

Is there a work around to this, known/documented albeit unexpected (to me) behavior?

like image 792
zetsu Avatar asked May 07 '16 10:05

zetsu


1 Answers

Until the issues KT-5870, KT-9772 are resolved you can do the following:

operator fun <V, V1 : V> (() -> Map<in String, V>).getValue(thisRef: Any?, property: KProperty<*>): V1 {
    val map = this()
    return map[property.name] as V1
}

Which can then be used as follows:

class Mapped(var data:Map<String,String>){
    val firstName:String by { data }
}

The above does not handle nullability well. Here's an improved version:

operator fun <V, V1 : V> (() -> Map<in String, V>).getValue(thisRef: Any?, property: KProperty<*>): V1 {
    val map = this()
    val key = property.name
    @Suppress("UNCHECKED_CAST")
    val value = map[key] as V1
    if (property.returnType.isMarkedNullable) {
        return value
    } else {
        if(value != null){
            return value
        }
        if(map.containsKey(key)){
            throw KotlinNullPointerException("Property baking map returned null value for key '$key' for non nullable property: $property")
        } else {
            throw KotlinNullPointerException("Property baking map has no key '$key' for non nullable property $property")
        }
    }
}
like image 64
miensol Avatar answered Jan 04 '23 00:01

miensol