Kotlin 类型推断失败

Kotlin type inferrence failed

这个科特林代码:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text
    if (false) {
        temp = Arrays.deepToString(arrayOf(text))
    } 

    return temp.isBlank() // <-- only safe (?.) or non null asserted (!!.) calls
}

不编译消息:only safe (?.) or non null asserted (!!.) calls are allowed on a nullable receiver of type String?

但是如果我添加 else:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text
    if (false) {
        temp = Arrays.deepToString(arrayOf(text))
    } else {
        temp = Arrays.deepToString(arrayOf(text))
    }

    return temp.isBlank()
}

全部编译。那么,为什么类型推断失败了?

如果我将临时类型更改为 var temp: String = text,它就会成功复制!因此,此外,如果我们像这样更改 temp 的分配:temp = String.format("%s", text) 它也会被编译。

更新:

编译成功:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text
    if (false) {
        temp = String.format("%s", text)
    } 

    return temp.isBlank() // <-- only safe (?.) or non null asserted (!!.) calls
}

还有这个:

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp: String = text
    if (false) {
        temp = Arrays.deepToString(arrayOf(text))
    } 

    return temp.isBlank() // <-- only safe (?.) or non null asserted (!!.) calls
}

通过将 text 分配给 temp,temp 的类型也将变为 String?。像这样:

var temp: String? = text

因为你的 if (false) 永远不会被执行,所以它对代码没有任何影响。 else 添加了一个总是被执行的块(因为它基本上意味着 if (true))并分配一个 Stringtemp 是不可空的。由于在第二个示例中 temp 不可为空,因此您不必再使用 safe-call 运算符...

你可能会想

if (text == null) {
    return true
}

text 的类型被细化为 String 而不是 String?

但好像不是;相反,当编译器看到 text 在需要 String 的地方使用时,它会插入一个智能转换。在

var temp = text

行没有理由插入强制转换,所以编译器没有,temp的类型是String?.

如果你写

var temp: String = text

强制转换 必需的,因此编译器确实会插入它。

如果你写

if (...) {
    temp = Arrays.deepToString(arrayOf(text))
} else {
    temp = Arrays.deepToString(arrayOf(text))
}

编译器发现无论发生什么,temp 都被分配了一个值 platform type String!,它又可以是 smart-cast 到 String。没有 else 分支,这不会发生。

编辑:

奇怪的是,如果您只是删除 if 并留下

fun badKotlin(text: String?): Boolean {
    if (text == null) {
        return true
    }

    var temp = text
    
    return temp.isBlank()
}

它确实可以编译,如果我的解释是完整的,我不希望它编译。所以编译器确实维护了智能转换所需的信息,但它似乎没有被应用,因为

More specifically, smart casts are applicable according to the following rules: ...

  • var local variables - if the variable is not modified between the check and the usage, is not captured in a lambda that modifies it, and is not a local delegated property;

if-else情况下,两个分支中的赋值一起作为另一个检查;在 if-only,一个分支没有。