Add possibility to cast EmptyMap to empty instance of any Map realization


#1
val hashMap = HashMap<String, String>()
//hashMap.put("key", "value")               <--------------
val resultHashMap = hashMap.keySet()
    .map{doSomeWorkReturningListOfpairs()}
    .toMap() as HashMap<String, SomeObject>

If I uncomment hashMap.put("key", "value") line then this code correctly return HashMap object, but with empty hashMap this code throw exception

kotlin.collections.EmptyMap cannot be cast to java.util.HashMap

And Idea does not shows any warning about this unknown behavior of toMap realization


#2

You cannot rely on the type of collection that will be returned by the map operations. If you need a mutable map, replace toMap() with toMutableMap(). Note that this will return a copy.


#3

If I can’t rely on return of a function why it function for?

It absurd situation what on same input object with difference only in elements count I receive two incompatible returns.

However Idea may warning about dangerous cast, but did not do it


#4

Okay, maybe this is clearer: You cannot rely on the actual type that is being returned. The only thing that is guaranteed to work is the declared return type of the function.

The reason you may get different implementations as the final result, is because your start situation is different: an empty map versus a map with at least 1 element.


#5

We talk about different things.

You talk about how it actually work, but I talk about changing behavior of EmptyMap.

It work this way becouse it work this way - is not an argument.

EmptyMap - is an instance of Map with zero elements. Why I can’t simply cast it to another Map realization with zero elements?


#6

Yes, I talk about how it actually works, because it was designed to work this way. So, it is not possible to make random changes to the design because it does not fit your particular scenario. In this case, the design choice was to let the collection operators always return immutable collections.

Why is operating on immutable collections a good thing? Because the operators can then return optimized implementations of the collections, and these optimized collections may have very specific implementations for the operators that get called upon them. This (can) result in more efficient code in terms of memory and/or speed.

It is something you will have to live with. If you want to do it more efficiently, or you are forced to work with types that the standard functions do not return, you can either do it inefficiently by creating copies or you write code to do it in the way that you need.

For example, this is efficient and works with HashMap:

val sourceMap = HashMap<String, String>()
// Do whatever you want here: Leave it empty, fill it to the brim, etc.
val destinationMap = HashMap<String, SomeObject>()
sourceMap.keys.forEach { key ->
    destinationMap.put(key, doSomeWorkReturningSomeObjectToAssociateWith(key))
}

#7

If the function signature of toMap() states it will return a Map, you cannot assume it will return a HashMap. You can only assume it will return a Map. As it happens, the current implementation sometimes returns a HashMap, but it’s wrong to rely on it.

If you need a MutableMap, you should use toMudatbleMap() instead. There is rarely a reason to depend on a particular implementation rather than on an interface.

But if you absolutely need it to be a HashMap, you can create one and then collect the values into it like this: .mapTo(HashMap<String, SomeObject>()) or mapValuesTo(HashMap<String, SomeObject>())`.


#8

What you want coersion or automatic conversion. That is not part of the Kotlin language.

The reason why the program may want to return different types is because doing so may be more efficient in various ways. The whole point of the use of interfaces is that the using program cannot rely on the specific return type so the implementation can choose the best. Of course nothing stops you from creating your own toHashMap extension function that explicitly returns a hashmap.