Can not extend HashMap in Kotlin

I am trying to extend from HashMap in order to override the put Method and register some data.

I have the following class:

class SomeClassExtendingHashMap : HashMap<String, Object>() {

}

I receive the following compilation error:

SomeClassExtendingHashMap.kt: (6, 7): Inherited platform declarations clash: The following declarations have the same JVM signature (getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;):
    fun getOrDefault(key: Any!, defaultValue: Object!): Object!
    fun getOrDefault(key: String, defaultValue: Object): Object

Per my understand getOrDefault from Kotlin is conflicting with Map.getOrDefault.
Is there a better way to handle that?

What Kotlin version do you use?
Your sample compiles on 1.1-M02 (try with http://try.kotlinlang.org/)

1 Like

This is not an answer for your particular question, but you may also consider another approach, using delegation:

class SomeClassExtendingHashMap(val underlying:HashMap<String, Any>) : MutableMap<String, Any> by underlying {
    override fun get(key: String): Any? {
        return underlying[key]
    }
}

I just ran into the same issue when compiling with Gradle 3.2.1 and Kotlin 1.0.5-3. With Gradle 3.1 and the same version of Kotlin, the same code compiles just fine.

Is this a Gradle regression?

I created this issue:

https://youtrack.jetbrains.com/issue/KT-15313

It looks like that in Kotlin 1.4 it’s still not possible.

class SomeClassExtendingHashMap : HashMap<String, Any>() {}

The compiler gives the error:

This type is final, so it cannot be inherited from

which I have some troubles understanding, since here’s how HashMap is defined, at least in common, js and jvm, in kotlin/libraries/stdlib :

common/src/kotlin/collections/HashMap.kt
8:expect class HashMap<K, V> : MutableMap<K, V> {

js/src/kotlin/collections/HashMap.kt
19:public actual open class HashMap<K, V> : AbstractMutableMap<K, V>, MutableMap<K, V> {

jvm/src/kotlin/collections/TypeAliases.kt
15:@SinceKotlin("1.1") public actual typealias HashMap<K, V> = java.util.HashMap<K, V>

Where does the ‘final’ modifier come from?

I’m not sure why HashMap would show non-final in this case but I wanted to highlight how to do this with delegation again for future readers:

class MyMap<K, V> : MutableMap<K, V> by HashMap<K, V>()

It’s good to remember that it’s a code smell to type variables as specified collection implementations. If you ever find yourself using ArrayList instead of List, or HashMap instead of Map it probably indicates your doing something wrong.
For example:

val a: HashMap<String, String> = HashMap() // likely code smell
val b: Map<String, String> = HashMap() // good

EDIT: To clarify, both a and b are HashMaps, but since in this case we don’t care about the details of what kind of map (e.x. we don’t care about big-O performance), we want to consider them as Maps.

1 Like

@arocnies Future readers may, like me, be stuck with classic inheritance: the delegation pattern is certainly fantastic, but is not always applicable, especially when you have several constructors. And inheriting from a concrete collection class is definitely not a code smell.

1 Like

I wasn’t saying inheriting from a concrete collection is a code smell, I meant to say that typing variables, params, or return types as concrete collections is a code smell :slight_smile:

Of course just because typing variables/params/return types to the class instead of the interface smells doesn’t mean it’s wrong–there are plenty of cases where it’s good to specify your collection implementation. You’ll want to type it to a specific concrete class if you’re writing an algorithm and trying to get specific big-O performance.
For some reason, it’s a common mistake for newer coders to do this at times where it’s incorrect.

EDIT: Although subclassing HashMap isn’t a code smell in itself, subclassing anything to get its functionality usually is (which is the point “composition over inheritance”). For example, I once had a professor tell his students to subclass List in order to implement other collections–which was definitely wrong. If I was creating a custom Map collection in Java, unless I very specifically wanted it to be considered “is a” HashMap, I’d still use Java delegation to a private member of my class and extend the Map interface.

2 Likes