在当前的 Kotlin(1.0 测试版或更高版本)中,您不再遇到此问题。您的代码将编译。 val 或var 的局部变量可以安全地Smart Cast,因为编译器可以确定该值是否可能发生突变(例如在另一个线程上)。
这是来自another Stack Overflow question 的摘录,涵盖了可空性的更多方面以及处理这些问题的 Kotlin 运算符。
更多关于 null 检查和智能投射
如果您使用null 检查保护对可空类型的访问,编译器会将smart cast 语句主体内的值设为不可为空。有一些复杂的流程不会发生这种情况,但对于常见的情况可以正常工作。
val possibleXyz: Xyz? = ...
if (possibleXyz != null) {
// allowed to reference members:
possiblyXyz.foo()
// or also assign as non-nullable type:
val surelyXyz: Xyz = possibleXyz
}
或者,如果您执行is 检查不可为空的类型:
if (possibleXyz is Xyz) {
// allowed to reference members:
possiblyXyz.foo()
}
对于同样可以安全转换的“when”表达式也是如此:
when (possibleXyz) {
null -> doSomething()
else -> possibleXyz.foo()
}
// or
when (possibleXyz) {
is Xyz -> possibleXyz.foo()
is Alpha -> possibleXyz.dominate()
is Fish -> possibleXyz.swim()
}
有些东西不允许null检查到smart cast以供以后使用变量。上面的示例使用了一个在应用程序流程中绝不可能发生变异的局部变量,无论是val 还是var,这个变量都没有机会变异为null。但是,在编译器无法保证流分析的其他情况下,这将是一个错误:
var nullableInt: Int? = ...
public fun foo() {
if (nullableInt != null) {
// Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
val nonNullableInt: Int = nullableInt
}
}
变量nullableInt的生命周期并不完全可见,可能是从其他线程分配的,null检查不能将smart cast变成一个不可为空的值。有关解决方法,请参阅下面的“安全呼叫”主题。
另一个不能被smart cast 信任而不发生变异的情况是具有自定义getter 的对象上的val 属性。在这种情况下,编译器无法看到改变值的原因,因此您将收到一条错误消息:
class MyThing {
val possibleXyz: Xyz?
get() { ... }
}
// now when referencing this class...
val thing = MyThing()
if (thing.possibleXyz != null) {
// error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
thing.possiblyXyz.foo()
}
阅读更多:Checking for null in conditions