I'm new to Kotlin and these two below codes give different results.
fun main() {
var name: String? = "Rajat"
name = null
print(name?.toLowerCase())
}
Output: Compilation Error (illegal access operation)
fun main() {
var name: String? = null
print(name?.toLowerCase())
}
Output: null
CodePudding user response:
When you do this assignment:
name = null
name
is smart casted to Nothing?
, which is problematic. Nothing
is the subtype of every type, and so you become able to call any accessible extension functions of any type, according to the overload resolution rules here.
Compare:
fun main() {
var name: String? = "Denis"
name = null
print(name?.myExtension()) // works
val nothing: Nothing? = null
print(nothing?.myExtension()) // also works
}
fun Int.myExtension(): Nothing = TODO()
Note that allowing you to call any extension function on Nothing
is perfectly safe - name
is null anyway, so nothing is actually called.
Char.toLowerCase
and String.toLowerCase
happen to be two of the extension functions that are accessible, and you can call both on name
, which is now a Nothing?
. Therefore, the call is ambiguous.
Note that smart casts only happens in assignments, not in initialisers like var name: String? = null
. Therefore, name
is not smart casted to Nothing?
in this case:
fun main() {
var name: String? = null
print(name?.toLowerCase()) // better to use lowercase(), toLowerCase is deprecated!
}
For the reason why, see my answer here.
CodePudding user response:
The actual error on your first example is
Overload resolution ambiguity: public inline fun Char.toLowerCase(): Char defined in kotlin.text public inline fun String.toLowerCase(): String defined in kotlin.text
Looks like the Kotlin compiler is being too smart for its own good here. What's happening, is that on the second example, you are explicitly defining a variable of type String?
and assigning it some value (null
in this case, but that doesn't matter).
On the second example, you are defining a variable of some type, and then telling the compiler "hey, after this assignment, name
is always null
". So then it remembers the more-specific "name
is null
" instead of "name
is String?
".
The standard library has two methods called toLowerCase
, one on Char
and one on String
. Both of them are valid matches now, and the compiler is telling you it doesn't know which one to pick. In the end that won't matter, because name
is null
, but the compiler apparently doesn't use that final thing to throw out the method call altogether.