我有一个可以为空的字符串变量ab
。如果我在给它分配null之后通过安全调用操作符调用toUpperCase
,kotlin会给出错误。
fun main(args: Array<String>){
var ab:String? = "hello"
ab = null
println(ab?.toUpperCase())
}
错误:(6,16) 过载分辨率模糊: @InlineOnly public inline fun Char.toUpperCase():在kotlin.text中定义的char @InlineOnly public inline fun String.toUpperCase():在kotlin.text中定义的字符串
这有什么问题?
正如本doc about smart-casts所述:
x = y使赋值后的y类型为x
线ab = null
可能聪明的铸造ab
到Nothing?
。如果你检查ab is Nothing?
它确实是true
。
var ab: String? = "hello"
ab = null
println(ab?.toUpperCase())
println(ab is Nothing?) // true
由于Nothing?
是所有类型的子类型(包括Char?
和String?
),它解释了为什么你得到Overload resolution ambiguity
错误。这个错误的解决方案将是Willi Mentzel在his answer中提到的,在调用ab
之前将String
转换为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()
这看起来真的像个bug。在分配String?
时,类型null
以某种方式丢失,所以你必须明确地告诉编译器它应该处理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
}
我不确定,但由于智能投射(对Nothing?
,每种可空类型的子类型),这似乎是一个错误。这个工作:
fun main(args: Array<String>) {
var ab: String? = "hello"
ab = makeNull()
println(ab?.toUpperCase())
}
fun makeNull(): String? = null
唯一的区别:编译器不直接知道null
赋值,这似乎导致你的例子中的错误。但是,你的应该也应该工作。
我相信这是由于Kotlin使用的smart casts。换句话说,Kotlin能够推断出这行代码:
ab = null
变量ab
的类型只是null
(这不是你可以在Kotlin中使用的实际类型 - 我只是指允许值的范围),而不是String?
(换句话说,没有办法ab
可能包含String
)。
考虑到toUpperString()扩展函数仅为Char和String(而不是Char?或String?)定义,因此无法在它们之间进行选择。
要避免这种行为,请查看其他人提出的答案(例如显式转换为String?),但这绝对看起来像一个功能(而且非常有用),而不是我的错误。
我反编译你的功能,我想:在你制作ab = null
之后,编译器将智能播放它,将null
(ACONST_NULL)
放在ab
的每一个版本中。然后因为null
没有类型。你不能推断toUpperCase()
接收器的类型。
这是从kotlin字节代码生成的java等效代码:
public final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String ab = "hello";
ab = (String)null;
Object var3 = null;
System.out.println(var3);
}
这看起来应该由kotlin团队解决。