Home > Enterprise >  Override kotlin operator extension function in subclass
Override kotlin operator extension function in subclass

Time:09-09

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

  • Related