android - 访问 Android 传感器的惰性不可空属性时的空属性

标签 android kotlin kotlin-android-extensions

初始化 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? Warning message

在分配 nullable 时它不应该引发编译时错误val这是non-null

Resolved


当值为proximitySensor被访问,如果它为 null,它会按预期产生一个运行时异常'java.lang.String android.hardware.Sensor.getName()' on a null object reference

lazy()在 Kotlin 标准库引用中如下:

  1. lazy()返回 Lazy<T>存储 lambda 初始值设定项的实例。
  2. getter 的第一次调用执行传递给 lazy() 的 lambda和 存储其结果。
  3. 随后,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/

相关文章:

android - 无法覆盖 Kotlin 中的 getParam (Volley)

eclipse - Kotlin 无法使用 Eclipse 工作区解析

android - Varargs Kotlin Java 互操作无法正常工作

java - 如何更新所有 fragment ? onProgressUpdate 不更新所有 fragment

android - android模拟器中的条码扫描

java - ColorFilter 仅特定颜色

相同 Activity hide() 的 Android FragmentManager 不起作用

csv - Kotlin 反射 - 从 CSV 创建对象

java - 找不到类 'kotlin.jvm.internal.DefaultConstructorMarker'

java - 如何在 Android Activity 上使用 Kotlin 扩展函数正确地进行空值检查