I am trying to write my own subclass of LinkedHashMap that executes some additional functionality on update. This looks something like the following:
class MyMap(): LinkedHashMap<String, String>() {
operator fun set(key: String, value: String) {
super.put(key, value)
doSomethingWith(value)
}
}
I then declare a variable of type MutableMap<String, String>
and assign an object of type MyMap
to it:
val myMap: MutableMap<String, String> = MyMap()
However, if I now try to use my modified operator method
myMap["key"] = "value"
the method I wrote is not called, but the extension function defined for MutableMap in kotlin (kotlin/collections/Maps.kt
) is. This part I don't understand, since the declared type of my variable is an interface (so statically resolving the method should not be possible), and the dynamic type is MyMap
, which defines the set
operator and doesn't have an extension function for it (which should either way not be able to hide an actual implementation in the class itself). If I declare the static type of my variable as MyMap
, my function is called as expected. Why does Kotlin resolve the function via the parent class if the declared type of my variable is different?
DISCLAIMER: I am going to solve this problem by overriding the put
function, so my question is less about a concrete solution and more about finding an explaination for this imo weird behavior.
CodePudding user response:
Your set
method does not override
anything in any of its superclasses or any of the interfaces it implements, so dynamically resolving to your set
method through the MutableMap
interface is not possible in the first place. In fact, there is no instance method set
in the hierarchy for you to override. set
is an extension function!
operator fun <K, V> MutableMap<K, V>.set(key: K, value: V)
Basically, the above extension function and the set
you wrote are two unrelated callables. The compiler only sees the former when the compile time type is MutableMap
.
It resolves to your set
when the compile time type is MyMap
because it always prioritises non-extension functions over extension functions. See here for the exact priorities.
In this case, you can actually override (in the general sense, not override
) the built-in set
by just redeclaring your own extension with the same signature:
operator fun <K, V> MutableMap<K, V>.set(key: K, value: V) {
...
}
This is because extension functions in the package scope and explicitly imported extension functions has a higher priority than implicitly imported extension functions. The built-in set
, being in the kotlin.collections
package, is implicitly imported.
But if you want dynamic resolution, that's not going to work.
CodePudding user response:
It's because you declared your variable as MutableMap<String, String>
Just changing this line
val myMap: MutableMap<String, String> = MyMap()
to
val myMap = MyMap()
or
val myMap : MyMap = MyMap()
should solve it