I have a nullable string variable ab
. If I call toUpperCase
via safe call operator after I assign null to it, kotlin gives error.
fun main(args: Array<String>){
var ab:String? = "hello"
ab = null
println(ab?.toUpperCase())
}
Error:(6, 16)
Overload resolution ambiguity:
@InlineOnly public inline fun Char.toUpperCase(): Char defined in kotlin.text
@InlineOnly public inline fun String.toUpperCase(): String defined in kotlin.text
What's the problem here?
I'm not sure but that seems to be a bug due to smart casting (to Nothing?
, subtype of every nullable type). This one works:
fun main(args: Array<String>) {
var ab: String? = "hello"
ab = makeNull()
println(ab?.toUpperCase())
}
fun makeNull(): String? = null
The only difference: The compiler does not know the null
assignment directly, which seems to cause the error in your example. But still, yours should probably work too.
As stated in this doc about smart-casts:
x = y makes x of the type of y after the assignment
The line ab = null
probably smart casts ab
to Nothing?
. If you check ab is Nothing?
it is indeed true
.
var ab: String? = "hello"
ab = null
println(ab?.toUpperCase())
println(ab is Nothing?) // true
Since Nothing?
is subtype of all types (including Char?
and String?
), it explains why you get the Overload resolution ambiguity
error. The solution for this error will be what Willi Mentzel mentioned in his answer, casting ab
to the type of String
before calling toUpperCase()
.
//interface
interface A {}
interface B {}
//extension function
fun A.x() = 0
fun B.x() = 0
//implementing class
class C : A, B {}
C().x() //Overload resolution ambiguity
(C() as A).x() //OK. Call A.x()
(C() as B).x() //OK. Call B.x()
It really seems like a bug. The type String?
is lost somehow upon assigning null
, so you have to tell the compiler explicitely that it should deal with a String?
.
fun main(args: Array<String>){
var ab: String? = "hello"
ab = null
println((ab as String?)?.toUpperCase()) // explicit cast
// ...or
println(ab?.let { it.toUpperCase() }) // use let
}
I believe this is due to smart casts used by Kotlin. In other words, Kotlin is able to infer that after this line of code:
ab = null
type of variable ab
is simply null
(this is not actual type you can use in Kotlin - I am simply referring to range of allowed values), not String?
(in other words, there is no way ab
might contain a String
).
Considering that toUpperString() extension function is defined only for Char and String (and not Char? or String?), there is no way to choose between them.
To avoid this behaviour see answers proposed by other guys (e.g. explicit casting to String?), but this definitely looks like a feature (and quite a useful one) rather than a bug for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With