【问题标题】:Kotlin type inferrence failedKotlin 类型推断失败
【发布时间】:2018-08-21 05:45:50
【问题描述】:

这个 kotlin 代码:

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

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

如果我将 temp 的类型更改为 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
}

【问题讨论】:

  • var temp = text 意味着temp 可以为空,而从未发生过的if(false) 块不会使其变为非空。在情况 2 中,Arrays.deepToString(arrayOf(text)) 始终执行,以使 temp 不为空。
  • 是的,但请参阅the comment 来回答
  • 我建议改用isNullOrBlank()方法。
  • var temp: String = texttemp = String.format("%s", text) 使 temp 不为空。因此,您的代码可以编译。 var temp: String = text 表示temp 是String,不是null,取值来自textvar temp: String? = text 表示temp 是字符串,可以为空,并且取值来自text
  • 那么,我的问题是为什么String.format("%s", text) 使temp 不为空,而Arrays.deepToString(arrayOf(text)) 使temp 可以为空?

标签: kotlin type-inference kotlin-null-safety


【解决方案1】:

你可能会这么想

if (text == null) {
    return true
}

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

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

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!,它可以再次智能转换为String。如果没有 else 分支,这不会发生。

编辑:

奇怪的是,如果你删除 if 并离开

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

    var temp = text
    
    return temp.isBlank()
}

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

更具体地说,智能转换根据以下规则适用:...

  • var 局部变量 - 如果变量在检查和使用之间没有修改,则不会在修改它的 lambda 中捕获,并且不是本地委托属性;

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

【讨论】:

  • 谢谢,很有趣...您对if-else 构造的问题样本有何看法?为什么会有编译?
  • 哇,我想相信你,所以你能提供关于必要和不必要的智能铸造的链接吗?
  • 这只是我的解释。文档在kotlinlang.org/docs/reference/typecasts.html#smart-casts,但不是很详细。
【解决方案2】:

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

var temp: String? = text

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

【讨论】:

  • 嗯,但是如果我将 temp 的类型更改为 var temp: String = text,它就会成功编译!因此,此外,如果我们像这样更改temp 的赋值:temp = String.format("%s", text) 它也会被编译。
  • 有趣的部分是为什么if (text == null) { return true } 不能将text 变成String
猜你喜欢
  • 2020-12-30
  • 2018-05-15
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多