Search code examples
kotlingenericscovariance

Kotlin class hierarchy and (co)variance


In my setup, I'm trying to have an interface Table that inherits from Map (because it will mostly be used as a wrapper around the map). Two classes inherit from Table - local and global. The global one will have a mutable map, and the local one will have a map of only local entries.

// entries
sealed class Entry {
    class EntryLocal : Entry
    class EntryGlobal : Entry
}

interface Table : Map<String, Entry> {
    fun getRecursive(key: String): Entry?   
}

class GlobalTable(val map:MutableMap<String, Entry>) : Table, Map<String, Entry> by map {
    override fun getRecursive(key: String) = this[key]

    ...
}

class LocalTable(
    private val parent: Table,
    val map: Map<String, EntryLocal>
) : Table, Map<String, EntryLocal> { // gives error
    override fun getRecursive(key: String): Entry? = map[key] ?: parent.getRecursive(key)

}

I get the following error:

Type parameter V of 'Map' has inconsistent values: Entry, EntryVar

Why is that? Doesn't Map<String, EntryLocal> inherit from Map<String, Entry>?


Solution

  • You're right the value type of Map is covariant and so a Map<String, EntryLocal> is a Map<String, Entry>.

    However, that's not your problem here. The problem is that LocalTable inherits from both Map<String, EntryLocal> (directly) and Map<String, Entry> (via Table) so it's not clear what the value type should be in LocalTable.

    In other words, what's the return type of LocalTable.get? Is it Entry or EntryLocal?

    Here's the problem in a nutshell:

    interface M<T> {}
    interface A : M<String> {}
    interface B : M<Object> {}
    class C : A, B {}
    

    You'll get the same error saying that parameter T of M has inconsistent values. Even though String is an Object, Kotlin won't assume that the type parameter T of the base class M should therefore be String (or Object).