Why is the key type parameter of a Kotlin Map invariant?

Viewed 148

The Map interface in Kotlin (using V1.6.21) has a signature of

interface Map<K, out V>

Why is K invariant instead of covariant (out K)?

The documentation of type parameter K says:

The map is invariant in its key type, as it can accept key as a parameter (of containsKey for example) and return it in keys set.

However, interface Set is covariant in the element type, so the the last part ("return it in keys set") is not applicable, at least not immediately.

Further, the type parameter K is used only at occurrences where the map state is not modified, for lookup purposes (methods containsKey, get, getOrDefault). At these places, isn't it safe to use @UnsafeVariance? After all, that same technique was employed to Map's value type parameter V, for example in containsValue, to allow making V covariant.

1 Answers

My guess would be that using a Map<KSubtype, V> as a Map<KSupertype, V> (where KSubtype : KSupertype) does not really make a lot of sense because the former, by construction, cannot contain entries with keys other than KSubtype.

So a proper implementation should return null from all calls to get(kSupertype) as well as return false from those to containsKey(kSupertype).

In the case of Set<out E> it's only the contains function that needs unsafe variance, and Map would also require unsafe variance on get. This might have been too much of a peculiarity to support, compared to the value of supporting the use case.

Related