I';我在kotlin安全呼叫中收到过载解析模糊错误
我有一个可为空的字符串变量I';我在kotlin安全呼叫中收到过载解析模糊错误,kotlin,nullable,Kotlin,Nullable,我有一个可为空的字符串变量ab。如果我在为toUpperCase赋值null后通过安全调用操作符调用它,kotlin会给出错误 fun main(args: Array<String>){ var ab:String? = "hello" ab = null println(ab?.toUpperCase()) } fun main(args:Array){ 变量ab:字符串?=“您好” ab=null println(ab?.toUpperCase())
ab
。如果我在为toUpperCase
赋值null后通过安全调用操作符调用它,kotlin会给出错误
fun main(args: Array<String>){
var ab:String? = "hello"
ab = null
println(ab?.toUpperCase())
}
fun main(args:Array){
变量ab:字符串?=“您好”
ab=null
println(ab?.toUpperCase())
}
错误:(6,16)重载解析歧义:
@仅限在线公共 内联fun Char.toUpperCase():在kotlin.text中定义的字符
@InlineOnly public inline fun String.toUpperCase():在kotlin.text中定义的字符串
这里的问题是什么?我不确定,但这似乎是一个错误,因为智能强制转换(to
Nothing?
,每个可空类型的子类型)。这一个有效:
fun main(args: Array<String>) {
var ab: String? = "hello"
ab = makeNull()
println(ab?.toUpperCase())
}
fun makeNull(): String? = null
fun main(args:Array){
变量ab:字符串?=“您好”
ab=makeNull()
println(ab?.toUpperCase())
}
fun makeNull():字符串?=无效的
唯一的区别是:编译器不直接知道
null
赋值,这似乎导致了示例中的错误。但是,你的可能也会起作用 它看起来真的像个虫子。类型String?
在赋值null
时以某种方式丢失,因此您必须明确地告诉编译器它应该处理字符串?
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
}
fun main(args:Array){
变量ab:字符串?=“您好”
ab=null
println((ab作为字符串?.toUpperCase())//显式强制转换
//…或
println(ab?.let{it.toUpperCase()})//使用let
}
我认为这是由于Kotlin使用了此功能。换句话说,Kotlin能够推断在这行代码之后:
变量ab
的类型只是null
(这不是您在Kotlin中可以使用的实际类型-我只是指允许值的范围),而不是String?
(换句话说,不可能ab
包含字符串
)
考虑到toUpperString()扩展函数仅为Char和String(而不是Char?或String?)定义,因此无法在两者之间进行选择 要避免这种行为,请参阅其他人提出的答案(例如,显式转换为字符串?),但这对我来说显然是一个功能(非常有用),而不是一个bug。如本文所述: x=y使x成为赋值后的y类型 行
ab=null
可能会将ab
转换为Nothing?
。如果您检查ab是否为空?
它确实是true
var ab: String? = "hello"
ab = null
println(ab?.toUpperCase())
println(ab is Nothing?) // true
由于Nothing?
是所有类型(包括Char?
和String?
)的子类型,因此它解释了为什么会出现重载解析模糊性
错误。此错误的解决方案将是Willi Mentzel在中提到的,在调用toUpperCase()
之前,将ab
转换为String
类型。
评论: 当一个类实现了两个接口,并且两个接口都有相同签名的扩展函数时,就会发生这种错误:
//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()
我反编译了你的函数,我想:在你做出
ab=null
的那一刻之后,编译器将智能地对它进行转换,在ab
的每一次出现中放入null
(acost\u null)。然后asnull
没有类型。无法推断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团队解决的问题。“变量ab的类型为空”空不是一个类型!“考虑到toUpperString()扩展函数仅为Char和String(而不是Char?或String?)定义,因此无法在两者之间进行选择。”使用了安全调用运算符,因此在ab为null时,toUpperString()永远不会在ab上调用。“…ab不可能包含字符串)“如果我分配ab=”hello2“在空赋值之后。突然又出现了一种方式,它再次包含字符串。是的,没有可以使用的
null
类型,但在进行类型推断时,编译器可能会在下面使用“类”null
类型(基本上,想象一下Typescript,其中不允许显式使用null
)。当然,这只是一个假设,但实际行为与这个假设相当一致。我们需要Kotlin开发团队中的某个人的评论,以澄清smart Cast实际上是如何工作的。是的,编译器可以决定无论如何不调用toUpperCase()
,因此它可以完全忽略整个过程。然而,Kotlin开发人员决定进行这项检查。这是否是一个好的决定是个人偏好的问题,对我来说,进行这样的检查似乎相当合理。等等,ab
不是已经在第一行定义为可为空的字符串了吗?那么,为什么要将一个字符串转换为字符串呢?是否有必要?@donfuxx,因为ab
的类型被智能转换为Nothing?
,Nothing?
是String?
和Char?
的子类型。同时,还有一个扩展函数toUpperCase()
声明为receiver typeString
和Char
。因此,我们必须告诉编译器,我们希望调用扩展函数,并将String
作为接收器。天哪,刚刚在调试器中完成了问题的代码,并且ab为Nothing?
计算为true。所以“聪明”的演员阵容发生了我想。。但“哑巴”演员可能是个更好的名字;-)我更喜欢像其他答案一样将其视为一个kotlin bug,但仍然竖起大拇指,因为这个答案有助于分析:-)很好的分析,这完全是错误的sense@donfuxx这是一个非常复杂的案例。您什么时候为某个对象分配null
,然后执行安全调用?无论如何都不会执行该调用。如果有的话,我更喜欢一个编译器警告
public final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String ab = "hello";
ab = (String)null;
Object var3 = null;
System.out.println(var3);
}