为什么 if not null 不适用于可变变量?

Why does if not null not work for mutable variables?

此代码无法编译。

class MyClass {
        var w: String? = "Hello"
        init {
            if(w!=null) {
                println(w.length)
            }
        }
    }

编译器错误: 智能转换为 'String' 是不可能的,因为 'w' 是一个可变的 属性,它可以被这个改变时间。 这是什么意思? 类似的代码可以完美编译。

fun main(args: Array<String>) {
    var w: String? = "Hello"
    if(w!=null) {
        println(w.length)
    }
}

它们是相似的,因为根据我的理解,在这两种情况下,变量 w 将被实例化并且 if 块将紧随其后 运行。那么为什么这段代码可以完美编译?

在您的第一个代码块中,w 是一个 属性。在您的第二个代码块中,w 是一个局部变量。

可变 属性 不能智能转换,因为编译器无法确保 属性 的值在检查 type/nullability 和使用该值之间不会发生变化。

当你需要使用一个可空的属性时,为了避免做非空断言,你必须先将它复制到一个局部变量。您可以明确地或使用范围函数来执行此操作。

//Explicit
val localW = w
if (localW != null) {
    println(localW.length)
}

// The common ?.let pattern
w?.let { println(it.length) }

// run with function reference
w?.length?.run(::println)

一般来说,这些错误是因为编译器不能保证变量在 null 检查和假定它不为 null 的用法之间不能更改。在大多数情况下,这是因为另一个线程可以同时修改变量。

例如,另一个 init 块中可能有一些代码生成一个线程,该线程可以更改变量的值:

class MyClass {
    var w: String? = "Hello"

    init {
        thread {
            w = null
        }
    }

    init {
        if (w!=null) {
            println(w.length)
        }
    }
}

编译器不会检查所有可能产生类似影响的代码,因此它更倾向于保守并给您一个错误。

main方法的情况下,变量是局部的,因此编译器可以更容易地保证它不会被任何其他东西改变。

要解决此问题,您可以在访问 w 属性:

时创建一个中间局部变量
val myW = w
if (myW != null) {
    println(myW.length)
}

或使用let:

w?.let { nonNullW ->
    println(nonNullW.length)
}