Kotlin的逻辑“与”不会短路吗?

时间:2019-02-02 23:31:11

标签: kotlin

我一直遵循http://kotlinlang.org/docs/reference/null-safety.html#checking-for-null-in-conditions的Kotlin文档,并尝试改编此示例,

val b = "Kotlin"
if (b != null && b.length > 0) {
    print("String of length ${b.length}")
} else {
    print("Empty string")
}

对于b = null的情况。在IntelliJ Idea Kotlin项目中,我有一个app.kt,其main()函数定义为:

fun main() {
    val b = null
    if (b != null && b.length > 0) {
        print("String of length ${b.length}")
    } else {
        print("Empty string")
    }
}

但是,当我运行它时,出现两个编译错误:

Information:Kotlin: kotlinc-jvm 1.3.20 (JRE 11+28)
Information:2019-02-02 15:07 - Compilation completed with 2 errors and 0 warnings in 1 s 921 ms
/Users/kurtpeek/IdeaProjects/HelloWorld/src/app.kt
Error:(3, 24) Kotlin: Unresolved reference: length
Error:(4, 37) Kotlin: Unresolved reference: length

我了解即使第一个条件b.lengthb != null,编译器仍在评估false。这让我感到惊讶,因为我认为第一步是在需要时“短路”布尔表达式,然后调用b.length“安全”。

例如,在Python中,您可以执行以下操作:

In [1]: "foo" == "bar" and what.the.heck
Out[1]: False

即使未定义what,它仍然有效,因为and不等于"foo""bar"停止了。

这真的是科特林的工作方式吗?似乎缺少Python的“短路”功能将是一个限制。

2 个答案:

答案 0 :(得分:8)

Kotlin的&&运算符将短路(就像Java一样),但仅在运行时会短路。您遇到的是一个编译时间错误。特别是在将Kotlin(或Java)与Python进行比较时,要记住的最大区别是Kotlin和Java是静态类型的,并且具有编译阶段。因此,如果类型不匹配,则会出现编译错误。

让我们一次浏览一下...

val b = "Kotlin"
if (b != null && b.length > 0) {
    ...
}

在这种情况下,Kotlin将正确推断出b是类型String,因为您已将其明确设置为String(“ Kotlin”)。在这里我们应该注意String类型永远不能包含null。知道这一点,您的b != null语句的if部分是不必要的。但是,在评估完该变量(始终为真)之后,它将评估b.length,因为bString,因此具有length属性。此示例应该可以很好地编译(我没有对其进行测试)。

然后下一个...

val b = null
if (b != null && b.length > 0) {
    ...
}

此代码将无法编译,让我们看看原因...

这段代码看起来确实非常相似,但有很大的不同。在这种情况下,因为您只是将b设置为null,所以Kotlin将推断bNothing?。它没有有关您希望b成为哪种类型的信息,并且已将其设置为null(并且由于它是val,因此它总是 为{{ 1}})。由于nullb,因此null可以为空。

因此,鉴于此,当我们编译b时,它将总是失败,因为b != null永远不可能是不是b的东西。可是等等!我们现在正在编译...,当我们遇到null时,由于b.length没有Nothing?属性,因此Kotlin将抛出编译错误

从本质上讲,通过将length设置为b而不提供类型提示,Kotlin将采用唯一的路径来推断类型-null

答案 1 :(得分:1)

在链接的文本中:“请注意,这仅在b是不可变的情况下有效(即在检查和用法之间未修改的局部变量,或者具有备用字段且不可覆盖的成员val)。”

val b=null是不可变的,但是由于无法推断或存储null的类型,因此不能将其用作有效快捷方式的来源。

如果您将代码更改为可为空的类型,并将其设置为null,则可以使用。