I'm confused by Kotlin's null safety features when it comes to maps. I have a Map<String, String>
. Yet I can call map.get(null)
and it returns null
to indicate that the key is not present in the map. I expected a compiler error, because map
is a Map<String, String>
and not a Map<String?, String>
. How come I can pass null
for a String
argument?
And a related question: is there any type of Map, be it a stdlib one or a third-party implementation, that may throw NullPointerException
if I call get(null)
? I'm wondering if it is safe to call map.get(s)
instead of s?.let { map.get(it) }
, for any valid implementation of Map
.
Update
The compiler does indeed return an error with map.get(null)
. But that is not because of null safety, but because the literal null
doesn't give the compiler an indication of the type of the parameter being passed. My actual code is more like this:
val map: Map<String, String> = ...
val s: String? = null
val t = map.get(s)
The above compiles fine, and returns null
. How come, when the key is supposed to be a String
which is non-nullable?
CodePudding user response:
The get
method in Map
is declared like this:
abstract operator fun get(key: K): V?
so for a Map<String, String>
, its get
method should only take String
s.
However, there is another get
extension function, with the receiver type of Map<out K, V>
:
operator fun <K, V> Map<out K, V>.get(key: K): V?
The covariant out K
is what makes all the difference here. Map<String, String>
is a kind of Map<out String?, String>
, because String
is a subtype of String?
. As far as this get
is concerned, a map with dogs as its keys "is a" map with animals as its keys.
val notNullableMap = mapOf("1" to "2")
// this compiles, showing that Map<String, String> is a kind of Map<out String?, String>
val nullableMap: Map<out String?, String> = notNullableMap
And this is why you can pass in a String?
into map.get
, where map
is a Map<String, String>
. The map
gets treated as "a kind of" Map<String?, String>
because of the covariant out K
.
And a related question: is there any type of Map, be it a stdlib one or a third-party implementation, that may throw NullPointerException if I call get(null)?
Notice that this get
is an extension function, available to every type that implements Map
, so no, there is no type of Map
that doesn't have this behaviour. There could be a type that implements a map, but doesn't implement Map
though, but that would be very unconventional and I'm not aware of anyone doing such things.
CodePudding user response:
when you try to run that code you won't get null
but you will get an error instead:
val map = mapOf<String, String>("f" to "g")
map.get(null)
You will get that error which means that you .get()
only accepts String
, and null
is not a String
, you can try to run the code in this kotlin playground here:
Type inference failed. The value of the type parameter K should be mentioned in input types (argument types, receiver type or expected type). Try to specify it explicitly.