You can't do this directly through the type system, you will have to use encapsulation to ensure the types of key and value are consistent. This problem exists in Java as well.
The solution given by others doesn't work, since you'd only be able to put one type of key/value into the map (the idea of having a Map<Key<T>, T>
is somewhat flawed):
val map = mutableMapOf<Key<String>, String>() // what to put as generic params here?
map[Key<String>()] = "blah" // works
map[Key<Int>()] = 3 // doesn't work!
map[Key<Int>()] = "blah" // compiler error (as intended)
or:
val map = mutableMapOf<Key<*>, Any?>()
map[Key<String>()] = "blah" // works
map[Key<Int>()] = 3 // works
map[Key<Int>()] = "blah" // works, but is wrong.
You'd have to for instance write your own encapsulation around a map that ensures a key always matches a value:
class MutableKeyMap {
private val map = mutableMapOf<Key<*>, Any?>() // private / encapsulated
operator fun <T> get(key: Key<T>): T {
return map[key] as T
}
operator fun <T> set(key: Key<T>, value: T): Unit { // Key's T must match value's type.
map[key] = value
}
}
(You could add an immutable super-interface if you'd like)
And then use that as a field:
data class MyClass(val b: Boolean,
val s: String,
val flags: MutableKeyMap)
...
val map = MutableKeyMap()
map[Key<String>()] = "blah" // works
map[Key<Int>()] = 3 // works
map[Key<Int>()] = "blah" // compiler error (as intended)