Home > Back-end >  Cannot implement java.util.Map
Cannot implement java.util.Map

Time:03-16

Given this interface (literally; I can't change it):

interface NamedMap<K, V> : java.util.Map<K, V> {
    val name: String
}

How do I implement it by extending HashMap?

class NamedHashMap<K, V>(override val name: String) : NamedMap<K, V>, java.util.HashMap<K, V>()

This gives two compilation errors:

Class 'NamedHashMap' is not abstract and does not implement abstract member
public abstract fun size(): Int defined in NamedMap

Class 'NamedHashMap' must override public open fun forEach(p0: BiConsumer<in K, in V>): Unit defined in NamedMap
because it inherits many implementations of it

Generating size():

override fun size(): Int {
    TODO("Not yet implemented")
}

Leads to:

Platform declaration clash: The following declarations have the same JVM signature (size()I):

  • public open fun <get-size>(): Int defined in NamedHashMap
  • public open fun size(): Int defined in NamedHashMap

Replacing java.util.Map with Map would fix the problem, if I could.

CodePudding user response:

I can't fully explain it, but it seems Kotlin really doesn't like referencing Java collections directly. For example, this is considered a type mismatch by the Kotlin:

val map: java.util.Map<String, String> = java.util.HashMap<String, String>()

Anyway, no matter if it is possible to extend HashMap or not, I suggest not doing this. It is always cleaner to use aggregation and/or delegation than inheritance. In this specific case it also solves your problem, although it still requires some silly casting:

class NamedHashMap<K, V> private constructor(
    override val name: String,
    private val delegate: java.util.Map<K, V>
) : NamedMap<K, V>, java.util.Map<K, V> by delegate {

    constructor(name: String) : this(name, java.util.HashMap<K, V>() as java.util.Map<K, V>)
}

CodePudding user response:

Edit: broot's answer is much simpler using a delegate, I would use that instead.

Your code would work if all classes involved are Kotlin collections, not Java, ie, this code compiles just fine:

interface NamedMap<K, V> : Map<K, V> {
    val name: String
}

class NamedHashMap<K, V>(override val name: String) : NamedMap<K, V>, HashMap<K, V>()

If NamedMap must implement java.util.Map, then I suggest trying this. I'm not sure if there's a better solution, but I've used this pattern many times to implement an interface based on another object.

interface NamedMap<K, V> : java.util.Map<K, V> {
    val name: String
}


class NamedHashMap<K, V>(override val name: String) : NamedMap<K, V> {
    private val map = HashMap<K, V>()
    override fun size() = map.size
    override fun isEmpty() = map.isNotEmpty()
    override fun containsKey(key: Any?) = map.containsKey(key)
    override fun containsValue(value: Any?) = map.containsValue(value)
    override fun get(key: Any?) = map[key]
    override fun put(key: K, value: V) = map.put(key, value)
    override fun remove(key: Any?) = map.remove(key)
    override fun putAll(m: Map<out K, V>) = map.putAll(m)
    override fun clear() = map.clear()
    override fun keySet() = map.keys
    override fun values() = map.values
    override fun entrySet() = map.entries
}
  • Related