我在 kotlin 安全调用时出现过载解析模糊错误

I'm getting Overload resolution ambiguity error on kotlin safe call

提问人:Al Mamun 提问时间:12/19/2017 最后编辑:BakaWaiiAl Mamun 更新时间:12/20/2017 访问量:5619

问:

我有一个可以为空的字符串变量。如果我在为其分配 null 后通过安全呼叫操作员进行调用,则 kotlin 会出错。abtoUpperCase

fun main(args: Array<String>){
    var ab:String? = "hello"
    ab = null
    println(ab?.toUpperCase())
}

错误:(6, 16)
过载解析歧义:
@InlineOnly public 内联乐趣 Char.toUpperCase(): 在 kotlin.text 中定义的字符 @InlineOnly 公共内联乐趣 String.toUpperCase(): 在 kotlin.text
中定义的字符串

这里有什么问题?

Kotlin 可为 null

评论


答:

3赞 s1m0nw1 12/19/2017 #1

我不确定,但由于智能转换(到,每个可为空类型的子类型),这似乎是一个错误。这个有效:Nothing?

fun main(args: Array<String>) {
    var ab: String? = "hello"
    ab = makeNull()
    println(ab?.toUpperCase())
}

fun makeNull(): String? = null

唯一的区别是:编译器不直接知道赋值,这似乎会导致示例中的错误。但是,你的可能也应该有效。null

2赞 Willi Mentzel 12/19/2017 #2

这似乎真的是一个错误。该类型在赋值时以某种方式丢失,因此您必须明确地告诉编译器它应该处理 .String?nullString?

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
}
1赞 code_x386 12/19/2017 #3

我相信这是由于 Kotlin 使用的智能转换。换句话说,Kotlin 能够在以下代码行之后推断出这一点:

ab = null

变量的类型很简单(这不是您可以在 Kotlin 中使用的实际类型 - 我只是指的是允许值的范围),而不是(换句话说,没有办法包含 )。abnullString?abString

考虑到 toUpperString() 扩展函数只为 Char 和 String(而不是 Char? 或 String?)定义,因此无法在它们之间进行选择。

为了避免这种行为,请参阅其他人提出的答案(例如,显式转换为 String?),但这绝对看起来像一个功能(并且非常有用),而不是对我来说是一个错误。

评论

1赞 Willi Mentzel 12/19/2017
“变量 ab 的类型只是 null” null 不是类型!
0赞 Willi Mentzel 12/19/2017
“考虑到 toUpperString() 扩展函数仅针对 Char 和 String 定义(而不是 Char?还是 String?),则无法在它们之间做出选择。 使用了安全调用运算符,因此如果 toUpperString() 为 null,则永远不会在 ab 上调用。
0赞 Willi Mentzel 12/19/2017
"...ab 不可能包含 String)“如果我在 null 赋值后分配 ab = ”hello2“ 怎么办。突然有一种方法可以再次包含一个字符串。
0赞 code_x386 12/19/2017
是的,没有可以使用的类型,但是编译器在进行类型推断时可能会在下面使用“某种”类型(基本上,想象一下不允许显式使用的 Typescript)。当然,这只是假设,但实际行为与这个假设相当一致。我们需要 Kotlin 开发团队的某个人发表评论,以澄清智能转换的实际工作原理。nullnullnull
0赞 code_x386 12/19/2017
是的,编译器可以决定无论如何都不会调用 to,因此它可以完全忽略整个事情。但是,Kotlin 开发人员还是决定进行此检查。这是否是一个好的决定是个人喜好的问题,对我来说,做出这样的检查似乎是相当合理的。toUpperCase()
6赞 BakaWaii 12/19/2017 #4

如本文档中关于智能投射的说明

x = y 使赋值后 y 类型的 x

这条线可能聪明地转换为 .如果你检查它确实是.ab = nullabNothing?ab is Nothing?true

var ab: String? = "hello"
ab = null
println(ab?.toUpperCase())
println(ab is Nothing?) // true

由于是所有类型(包括 和)的子类型,因此它解释了出现错误的原因。此错误的解决方案将是 Willi Mentzel 在他的回答中提到的,在调用之前转换为类型。Nothing?Char?String?Overload resolution ambiguityabStringtoUpperCase()


言论: 当一个类实现两个接口并且两个接口都具有相同签名的扩展函数时,就会发生这种错误:

//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()

评论

2赞 donfuxx 12/20/2017
等等,第一行不是已经定义为可为 Null 的字符串了吗?那么为什么要强制转换为 String?有必要吗?ab
1赞 BakaWaii 12/20/2017
@donfuxx 由于 的类型是智能强制转换为 和 是 和 的子类型。同时,有一个扩展函数声明了 receiver type 和 。因此,我们必须告诉编译器,我们要调用扩展函数作为接收器。abNothing?Nothing?String?Char?toUpperCase()StringCharString
0赞 donfuxx 12/20/2017
天哪,只是在调试器中单步执行问题的代码并计算为 true。我猜发生了如此“聪明”的演员阵容......但可能“愚蠢”演员会是一个更好的名字;-)我更愿意像其他答案一样将其视为 kotlin 错误,但仍然竖起大拇指,因为这个答案有助于分析:-)ab is Nothing?
0赞 s1m0nw1 12/20/2017
很好的分析,这完全有道理
1赞 Kirill Rakhman 12/21/2017
@donfuxx这是一个非常有建构的案例。你什么时候会分配给某件事,然后执行安全调用?无论如何,调用都不会执行。如果有的话,我更喜欢编译器警告,例如“ab 已知为 null,调用永远不会执行”。null
1赞 crgarridos 12/20/2017 #5

我反编译了你的函数,我想:在你制作编译器的那一刻之后,它会智能地转换它,把每一个 .然后 as 没有类型。您无法推断 的接收方的类型。ab = nullnull(ACONST_NULL)abnulltoUpperCase()

这是从 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 团队解决的问题。

评论

0赞 crgarridos 12/20/2017
注意:我用来代替能够编译/反编译代码toInttoUpperCase