初始化 val
时类型 Sensor
通过 lazy
编译器忽略返回值可以是 null
注意:val
声明为 Sensor
而不是 Sensor?
private val sensorManager by lazy { getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val proximitySensor: Sensor by lazy { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
也可以返回null
并且该方法未使用 @Nullable
注释因此,它不能在编译时检查。
此外,Android Studio 在检查 null 时给出了一个警告,因为我们没有将其声明为
Sensor?
在分配 nullable
时它不应该引发编译时错误到 val
这是non-null
?
当值为proximitySensor
被访问,如果它为 null,它会按预期产生一个运行时异常。
'java.lang.String android.hardware.Sensor.getName()' on a null object reference
lazy()
在 Kotlin 标准库引用中如下:
-
lazy()
返回Lazy<T>
存储 lambda 初始值设定项的实例。 - getter 的第一次调用执行传递给
lazy()
的 lambda和 存储其结果。 - 随后,getter 执行返回存储的值。
Shouldn't an exception be thrown when the assignment is made and not when we try to access it?
operator fun setValue(
thisRef: Any?,
property: KProperty<*>, value: String
) {
// assign
// An exception should be generated at this point when the assignment happens
}
最佳答案
应该有编译时错误吗?
Shouldn't it raise a compile time error when assigning a nullable to a val which is non-null?
是的,但这不是这里发生的事情 在这种情况下,您要分配一个 platform type为您的值(value)并告诉编译器将其视为不可为空。应该有 IllegalStateException
的运行时错误- 正如@MarkoTopolnik 指出的那样,这是一个错误。
错误
KT-8135 涵盖了此错误.这是与代表直接相关的链接问题,KT-24258 . 还有一个包含一些可运行示例的讨论主题 here .
什么时候会抛出运行时异常?
当你使用proximitySensor
时会触发错误以需要非空值的方式,例如:
val s: Sensor = proximitySensor
proximitySensor.someMethod()
println(proximitySensor.someProperty)
但是这样在属性初始化的时候不会抛出异常!我们可以解决这个问题...
为什么第一次初始化Lazy属性没有报错?
自 sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
在java中实现它返回一个platform type .因此,惰性委托(delegate)的类型是 Lazy<Sensor!>
.
Lazy 类运行初始化程序 lambda。保存 lambda 的结果,然后以 T
类型返回,即 Sensor!
.
解决方案
如果我们在调用 lazy
时显式声明类型参数功能:
private val proximitySensor: Sensor by lazy<Sensor> { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
那么一旦访问惰性值就会抛出异常。
其他选项
将 proximitySensor 声明为 nullable
警告会引导您朝着正确的方向前进,要么不检查是否为空,要么将其声明为可为空。通过将类型显式声明为可为空来告诉编译器该值应被视为可为空:
private val proximitySensor: Sensor? by lazy { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
在惰性初始化器中提供一个默认值
如您所述,sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
可能返回 null。我们可以返回一个默认值:
val proximitySensor: Sensor by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) ?: SOME_DEFAULT_VALUE
}
关于平台类型的说明
Kotlin 中的平台类型允许您(调用者)决定类型是可为 null 还是不可为 null 的,从而使使用 Java 更加实用。假设 Java 中的所有内容都可以为 null 的替代方案意味着您将不得不进行大量痛苦的 null 检查,即使 Java 代码从不返回 null。
*注意:如果您正在编写 Java 代码,则可以使用 @Nullable
和 @NonNull
为 Kotlin 编译器和其他静态分析工具提供元数据的注解。 Spring Framework 等库 have done this to their APIs .
这是 a talk由 Kotlin 开发人员 Andrey Breslav 撰写,深入探讨了平台类型互操作性的设计决策。
关于android - 访问 Android 传感器的惰性不可空属性时的空属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50173899/