Нулевое свойство при доступе к ленивому ненулевому свойству Android Sensor - PullRequest
0 голосов
/ 04 мая 2018

При инициализации 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 выдает предупреждение при проверке на ноль , поскольку мы не объявили его как Sensor? Warning message

Не должно ли это вызвать ошибку времени компиляции при назначении nullable для val, который равен non-null?

Решено


При обращении к значению proximitySensor и его нулевом значении выдается исключение времени выполнения , как и ожидалось. 'java.lang.String android.hardware.Sensor.getName()' on a null object reference

lazy() в справочнике стандартной библиотеки Kotlin следующим образом:

  1. lazy() возвращает Lazy<T> экземпляр, в котором хранится лямбда-инициализатор.
  2. При первом вызове getter выполняется лямбда, переданная lazy(), и сохраняет его результат.
  3. Впоследствии выполнение получателя возвращает сохраненное значение.

Не должно создаваться исключение при назначении, а не когда мы попытаемся получить к нему доступ?

operator fun setValue(
                    thisRef: Any?,
                    property: KProperty<*>, value: String
            ) {
                // assign 
                // An exception should be generated at this point when the assignment happens 
       }

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Должна ли быть ошибка времени компиляции?

Разве это не должно вызывать ошибку времени компиляции при присвоении нулю значения val, отличного от NULL?

Да, но это не то, что здесь происходит В этом случае вы присваиваете тип платформы своему значению и указываете компилятору обрабатывать его как ненулевое значение. Должна быть ошибка времени выполнения IllegalStateException - поскольку @MarkoTopolnik указывает, это ошибка .

ошибка

Эта ошибка покрыта KT-8135 . Вот связанная проблема, напрямую связанная с делегатами, KT-24258 . Здесь также есть тема для обсуждения с несколькими работающими примерами здесь .

Когда будет выдано исключение времени выполнения?

Ошибка будет вызвана, если вы используете proximitySensor способом, который требует значения, не равного нулю, например:

val s: Sensor = proximitySensor
proximitySensor.someMethod()
println(proximitySensor.someProperty)

Но это не приведет к исключению при инициализации свойства! Мы можем это исправить ...

Почему нет ошибки при первой инициализации свойства Lazy?

Так как sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) реализован в Java, он возвращает тип платформы . Поэтому делегат Lazy имеет тип Lazy<Sensor!>.

Класс Lazy запускает лямбду-инициализатор. Результат лямбда сохраняется и затем возвращается как тип T, который является Sensor!.

Решение

Если мы явно объявим params типа при вызове функции lazy:

private val proximitySensor: Sensor by lazy<Sensor> { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }

Тогда будет выброшено исключение, как только будет получено ленивое значение.

Другие опции

Объявить бесконтактный датчик как обнуляемый

Предупреждение направляет вас в правильном направлении, либо не проверяйте нулевое значение, либо объявляйте его обнуляемым Скажите компилятору, что значение следует рассматривать как обнуляемое, явно объявив тип обнуляемым:

private val proximitySensor: Sensor? by lazy { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }

Укажите значение по умолчанию внутри ленивого инициализатора

Как вы упомянули, sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) может возвращать ноль. Мы могли бы вернуть значение по умолчанию:

val proximitySensor: Sensor by lazy {
  sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) ?: SOME_DEFAULT_VALUE 
}

Примечание о типах платформ

Типы платформ в Kotlin делают работу с Java более прагматичной, позволяя вызывающей стороне решать, является ли тип обнуляемым или не обнуляемым. Альтернатива предположить, что все в Java является nullable, означала бы, что вам пришлось бы делать много мучительной проверки нуля, даже если код Java никогда не возвращает null.

* Примечание. Если вы пишете код Java, вы можете использовать аннотации @Nullable и @NonNull для предоставления метаданных компилятору Kotlin, а также другим инструментам статического анализа. Библиотеки, такие как Spring Framework , сделали это со своими API .

Вот доклад Андрея Бреслава, разработчика Kotlin, который подробно рассказывает об этом проектном решении типов платформ для обеспечения взаимодействия.

0 голосов
/ 04 мая 2018

Благодаря вашему вопросу это теперь подтвержденная ошибка в бэкэнде JVM компилятора Kotlin .


Более раннее содержание этого ответа:

Я сделал MCVE с вашей проблемой:

val sensorManager = SensorManager()
val proximitySensor: Sensor by lazy { sensorManager.sensor }

fun main(args: Array<String>) {
    println(proximitySensor)
}

Это код поддержки Java:

public class SensorManager {
    public Sensor getSensor() {
        return null;
    }
}

public class Sensor {}

И ... угадайте что? Это печатает null. Я называю "Котлин жук":)


Вы спрашиваете:

Не следует ли создавать исключение при выполнении назначения, а не при попытке получить к нему доступ?

В случае делегата lazy единственное назначение происходит при первом доступе к свойству. Это распространяется на SynchronizedLazyImpl.value getter, который содержит хорошо известный дважды проверенный идиом init init, и это единственный раз, когда запускается блок инициализатора, который вы предоставляете.

Даже без этого подробного обсуждения должно быть ясно, что суть делегата lazy состоит в том, чтобы отложить инициализацию до самого последнего возможного времени, которое является первым доступом к свойству. Правильная реализация обеспечит выдачу исключения до завершения оценки свойства, не допускающего значения NULL. Текущая реализация, к сожалению, неверна.

...