AlexanderShirshov
AlexanderShirshov

Reputation: 128

Kotlin non nullable map allows remove null

Why this code can be compiled and executed without erros?

val map = HashMap<Int, Long>()
val key :Int? = null
map.remove(key)

In MutableMap remove declared as accepting only non nullable key, so it shouldn't even compile. Is it a Kotlin type inference bug or am I missing something?

public fun remove(key: K): V?

Upvotes: 2

Views: 524

Answers (4)

IlyaMuravjov
IlyaMuravjov

Reputation: 2492

In this case, map.remove(key) doesn't not calls

public fun remove(key: K): V?

It calls an extension remove function:

public inline fun <@OnlyInputTypes K, V> MutableMap<out K, V>.remove(key: K): V? =
    @Suppress("UNCHECKED_CAST") (this as MutableMap<K, V>).remove(key)

This function documentation says that it allows to overcome type-safety restriction of remove that requires to pass a key of type K.


It allows overcoming type-safety restriction because the key of the entry you are removing does not have to be the same type as the object that you pass into remove(key); the specification of the method only requires that they be equal. This follows from how the equals() method takes in an Any as a parameter, not just the same type as the object.

Although it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, there are many places where this is not the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So, for example, according to the specification of the method, it is possible to have a MutableMap<ArrayList<Something>, Something> and call remove(key) with a LinkedList as an argument, and it should retrieve the key which is a list with the same contents. This would not be possible if this extension remove(key) didn't exist.[1]

Upvotes: 2

Marcin Orlowski
Marcin Orlowski

Reputation: 75629

Your code is perfectly fine as remove() allows nullable arguments - your map contents definition got nothing to it. When remove() is invoked, it would try to find matching requested key in the map and as it's not there (it's completely irrelevant why it's not there - it's valid case for key to be not present) nothing will happen. Where compiler will complain is on any attempt to put such key into your map. Then map definition kicks in and since it's known that nullable keys not allowed, such code won't even compile as this is clearly buggy code.

Upvotes: 4

AlexanderShirshov
AlexanderShirshov

Reputation: 128

Eventually asking this question helped to find another question with explanation. In short, what actually happens is call of the extension function which have it's own type inference.

Upvotes: 0

deHaar
deHaar

Reputation: 18568

Kotlin could warn or refuse to compile (would be good), but it doesn't (for now).

The reason for it being not as bad as it looks from a first glance is that you cannot put an Int? into a MutableMap<Int, Long> because

val map = HashMap<Int, Long>()
val key :Int? = null
map.put(key, 1) // <--- WON'T COMPILE [Type mismatch: inferred type was Int? but Int was expected]
map.remove(key)

Nevertheless, I think you are right by wondering about that method being compiled.

Upvotes: 1

Related Questions