使用 Kotlin 属性 委托时出现 NullPointerException (NPE)
NullPointerException (NPE) when using Kotlin property delegate with by
我有一个 class,它接受用户在文本字段中的输入,并使用提供的函数 class 将它们转换为任何 class
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)
}
}
我已经这样做了,所以当我有 TextViews 时我可以这样做
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)
您的 属性 委托本身(本例中的 tareWeightInput
)为空。
您可以通过检查堆栈跟踪来判断是这种情况:它指出由于 NPE 而失败的方法调用是 IntegerInputView.getValue
。由于这是为了检索 属性 的值而在委托上调用的 属性 委托方法调用,我们知道委托必须为 null。
您的 属性 委托是 View
,所以我怀疑它正在以某种方式动态检索,可能是 findViewById
调用的结果。您需要确保包含委托的变量在检索时是非空的。考虑这个类似的例子:
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 互操作性,这可能在您的示例中被 platform types 掩盖了。
我有一个 class,它接受用户在文本字段中的输入,并使用提供的函数 class 将它们转换为任何 class
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)
}
}
我已经这样做了,所以当我有 TextViews 时我可以这样做
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)
您的 属性 委托本身(本例中的 tareWeightInput
)为空。
您可以通过检查堆栈跟踪来判断是这种情况:它指出由于 NPE 而失败的方法调用是 IntegerInputView.getValue
。由于这是为了检索 属性 的值而在委托上调用的 属性 委托方法调用,我们知道委托必须为 null。
您的 属性 委托是 View
,所以我怀疑它正在以某种方式动态检索,可能是 findViewById
调用的结果。您需要确保包含委托的变量在检索时是非空的。考虑这个类似的例子:
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 互操作性,这可能在您的示例中被 platform types 掩盖了。