I have a class that takes user inputs in a text field and converts them to an any class using the supplied functions
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)
}
}
I have done this so that when I have TextViews I can do this
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() }
)
...
I have a fragment that has an above custom view and when I have
override var tareWeight: Kg?
get() = tareWeightInput.value
set(value) {
tareWeightInput.value = value
}
all works fine, by what I really want to do is
override var tareWeight: Kg? by tareWeightInput
adding these lines to 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(
...
When I build, run and load the fragment this I get the below stack trace. Where am I going wrong?
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)
Your property delegate itself (tareWeightInput
in this example) is null.
You can tell that this is the case by examining the stack trace: it notes that the method call that failed due to the NPE was IntegerInputView.getValue
. Since this is the property delegate method call being called on the delegate in order to retrieve the value of the property, we know that the delegate must be null.
Your property delegate is a View
, so I suspect it's being retrieved dynamically somehow, possibly to the result of a findViewById
call. You need to ensure that the variable containing the delegate is non-null at the moment it's retrieved. Consider this similar example:
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)
}
This crashes, because it tries to set up the delegate before the variable is initialized. In this case, Kotlin throws the error earlier, but this may be being masked in your example by platform types due to Kotlin/Java interop.