声明为不可为空的 Kotlin 属性即使具有初始化值也可以为空

Posted

技术标签:

【中文标题】声明为不可为空的 Kotlin 属性即使具有初始化值也可以为空【英文标题】:Kotlin property declared as non-nullable is nullable even if it has initialized value 【发布时间】:2020-06-19 23:14:14 【问题描述】:

这是一个非常有趣的情况。 我有一些android自定义视图。它具有一些属性“状态”,用于根据此属性更改复选框的可绘制状态。如您所见,此属性被声明为不可为空,我使用默认值“State.Regular”对其进行初始化。

class SomeCustomView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatCheckBox(context, attrs) 

    sealed class State 
        object Regular : State()
        object Specific : State()
    

    // todo: it will be nice to implement statesaving
    //       but it's okay for now
    var state: State = State.Regular
        set(value) 
            field = value
            refreshDrawableState()
        

    override fun onCreateDrawableState(extraSpace: Int): IntArray =
        super.onCreateDrawableState(extraSpace + 1).apply 
            val stateAttrRes = when(state) 
                State.Specific -> R.attr.some_custom_view_specific
                State.Regular -> R.attr.some_custom_view_regular
            

            View.mergeDrawableStates(this, intArrayOf(stateAttrRes))
        

但是当我们要使用这个视图时,它会因为这个异常而崩溃:

kotlin.NoWhenBranchMatchedException

我曾尝试调试 when 表达式,我注意到在“onCreateDrawableState”方法中,它没有使用默认值“State.Regular”进行初始化,而是使用“null”进行初始化,这就是为什么我们有这个“NoWhenBranchMatchedException” '。

你有什么想法为什么这个属性初始化为 null 以及如何解决这个问题?

【问题讨论】:

解决这个问题的小技巧:在里面添加 elvis when:when(state ?: State.Regular) ... 但这是一个拐杖,Android Studio 也会将其突出显示为未使用,但它可以在运行时运行 【参考方案1】:

问题在于AppCompatCheckBox 的构造函数(调用onCreateDrawableState)在初始化属性的SomeCustomView 之前被调用。如果您不需要自定义设置器,您可以使用 lateinit 并在 onCreateDrawableState 中初始化它;有了它,请考虑these workarounds(但对于仅此一个地方来说,它们可能太复杂了)。

【讨论】:

【参考方案2】:

这不仅适用于 Kotlin。你在Java中也有这个问题。所以最好通过检查它不为空或设置一个默认值来忽略它。

OnCreateDrawableState 在 Kotlin 的 init 块之前调用。初始化后,您的属性将初始化

【讨论】:

以上是关于声明为不可为空的 Kotlin 属性即使具有初始化值也可以为空的主要内容,如果未能解决你的问题,请参考以下文章

退出构造函数时,不可为空的属性必须包含非空值。考虑将属性声明为可为空

Spring(kotlin)控制器接收数据类的不可为空的参数为空

构造函数是在 C# 中初始化类中不可为空的属性的唯一方法吗?

Retrofit 在不可为空的字符串上返回 null (Kotlin)

使用默认值添加不可为空的列时的 Oracle 错误

Kotlin学习快速入门——空安全