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
}