NullPointerException (NPE) при использовании делегата свойства Kotlin с помощью

У меня есть класс, который принимает вводимые пользователем данные в текстовое поле и преобразует их в любой класс, используя предоставленные функции.

class GenericTextFieldDelegate<T>(
    private val initBlock: () -> TextView,
    private val getConversion: (String?) -> T?,
    private val setConversion: (T?) -> String? = { it?.toString() }
    ) {
    private val textView: TextView by lazy { initBlock() }

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T? =
        getConversion(textView.text?.toString())

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
        textView.text = setConversion(value)
    }
}

Я сделал это так, что когда у меня есть TextView, я могу это сделать

class IntegerInputView @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
    defStyleAttr: Int = 0
) : LinearLayout(context, attributeSet, defStyleAttr), DataInput<Int> {

override var value: Int? by GenericTextFieldDelegate(
    { inputET },
    getConversion = { it?.toIntOrNull() },
    setConversion = { it.toString() }
)
...

У меня есть фрагмент с указанным выше пользовательским представлением, и когда у меня есть

override var tareWeight: Kg?
    get() = tareWeightInput.value
    set(value) {
        tareWeightInput.value = value
    }

все работает нормально, я действительно хочу

override var tareWeight: Kg? by tareWeightInput

добавляя эти строки к IntegerInputView

...

operator fun getValue(thisRef: Any?, property: KProperty<*>): Int? = value

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int?) {
    this.value = value
}

override var value: Int? by GenericTextFieldDelegate(
...

Когда я создаю, запускаю и загружаю этот фрагмент, я получаю следующую трассировку стека. Где я ошибаюсь?

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Integer com.gameforeverything.storekeeper.customViews.IntegerInputView.getValue(java.lang.Object, kotlin.reflect.KProperty)' on a null object reference
        at com.gameforeverything.storekeeper.fragments.weighInFragment.WeighInFragment.getGrossWeight(Unknown Source:7)
        at com.gameforeverything.storekeeper.fragments.weighInFragment.WeighInPresenter.getNetWeight(WeighInPresenter.kt:40)

1 ответ

Решение

Ваша собственность делегирует себя (tareWeightInput в этом примере) имеет значение null.

Вы можете сказать, что это так, изучив трассировку стека: она отмечает, что вызов метода, который не удался из-за NPE, был IntegerInputView.getValue. Поскольку это вызов метода делегата свойства, который вызывается в делегате для получения значения свойства, мы знаем, что делегат должен иметь значение NULL.

Ваш делегат собственности является View, поэтому я подозреваю, что он каким-то образом извлекается динамически, возможно, в результате findViewByIdвызов. Вам необходимо убедиться, что переменная, содержащая делегат, не имеет значения NULL в момент ее получения. Рассмотрим аналогичный пример:

import kotlin.reflect.KProperty

class FooDelegate {
    private var value: Int = 0
    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int = value
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { this.value = value }
}

class Foo {
    private lateinit var delegate: FooDelegate
    val bar by delegate
    
    init {
        delegate = FooDelegate()
    }
}

fun main() {
    println(Foo().bar)
}

Это приводит к сбою, поскольку он пытается настроить делегата до инициализации переменной. В этом случае Kotlin выдает ошибку раньше, но в вашем примере это может быть замаскировано типами платформ из-за взаимодействия Kotlin/Java.

Другие вопросы по тегам