I want to use MutableMap
with defaults:
val myMap = mutableMapOf<String, Set<String>>().withDefault { mutableSetOf() }
but I can't use getOrImplicitDefault
method because withDefault
returns MutableMap<String, Set<String>>
type. Moreover, I can't cast to MutableMapWithDefault
interface because this is a private interface.
I can't use get
method either because it returns a nullable type. It is ok because this is a method on the MutableMap
interface (moreover it doesn't call defaultValue
callback for take default value).
Seems like this functionality is not correctly implemented in Kotlin, or I am using it wrong. So, how do I use withDefault
wrappers correctly?
As of Kotlin 1.0 a wrapper returned by withDefault
is only usable in property delegation use cases.
val map = mutableMapOf<String, Set<String>>().withDefault { mutableSetOf() }
var property: Set<String> by map // returns empty set by default
Looks like in Kotlin 1.1 this actually works if you use the getValue() function instead of the get()
function.
I've been looking for a way to return default value from a MutableMap, but also store it at the same time for future retrieval. The .withDefault
only returns the default value, but doesn't store it. Calling .getOrPut
every time I need to retrieve a value doesn't look like a good idea. I've came up with something like this:
val myMap = with(mutableMapOf<String, Set<String>>()) {
withDefault { key -> getOrPut(key, { mutableSetOf<String>() }) }
}
This calls getOrPut
within withDefault
wrapper on the backing MutableMap object, which puts the missing key-value pair into the map and returns it.
To encapsulate the behaviour you can create a delegated property provider.
/** Wraps a [MutableMap]. Will generate a [defaultValue] if none exists, and set it into the map. */
fun <K, V> mapWithPutDefault(defaultValue: (key: K) -> V): ReadWriteProperty<Any?, MutableMap<K, V>> =
object : ReadWriteProperty<Any?, MutableMap<K, V>> {
private var map: MutableMap<K, V> = with(mutableMapOf<K, V>()) {
withDefault { key -> getOrPut(key) { defaultValue(key) } }
}
override fun getValue(thisRef: Any?, property: KProperty<*>): MutableMap<K, V> = map
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MutableMap<K, V>) {
this.map = value
}
}
fun main() {
val myMap: MutableMap<String, String> by mapWithPutDefault { "some default value for $it" }
println("map is empty: $myMap")
// map is empty: {}
val someValue = myMap.getValue("my-key")
println("map returns a default value: $someValue")
// map returns a default value: some default value for my-key
println("map now has a value: $myMap")
// map now has a value: {my-key=some default value for my-key}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With