You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin类中init代码块执行时机误区及初始化异常排查

Kotlin类初始化顺序导致的NullPointerException问题解析

问题场景

运行Code A时触发NullPointerException,提示isRecordingState未初始化;将init代码块移至成员变量定义之后得到Code B,运行恢复正常。此前误以为Kotlin类中函数可任意排序,且init代码块会在对象初始化完成后执行、可任意放置,现需明确该理解的错误之处。

Code A

@HiltViewModel
class SoundViewModel @Inject constructor(
    @ApplicationContext private val appContext: Context,
    ...
): ViewModel() {

    init { beginSoundDensity() }   

    private val _timeXState = MutableStateFlow(0)
    
    var isRecordingState by mutableStateOf(false)
        private set

    private var myJob: Job?=null


    fun beginSoundDensity() {
        if (isRecordingState == false) {
            isRecordingState = true

            myJob?.cancel()
            ...
           
        }
    }
}

Result A

java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object androidx.compose.runtime.State.getValue()' on a null object reference
        at info.dodata.soundmeter.presentation.viewmodel.SoundViewModel.isRecordingState(SoundViewModel.kt:245)
        at info.dodata.soundmeter.presentation.viewmodel.SoundViewModel.beginSoundDensity(SoundViewModel.kt:81)
        at info.dodata.soundmeter.presentation.viewmodel.SoundViewModel.<init>(SoundViewModel.kt:39)

Code B

@HiltViewModel
class SoundViewModel @Inject constructor(
    @ApplicationContext private val appContext: Context,
    ...
): ViewModel() {

    private val _timeXState = MutableStateFlow(0)

    var isRecordingState by mutableStateOf(false)
        private set

    private var myJob: Job?=null
    
    init { beginSoundDensity() }

    fun beginSoundDensity() {
        if (isRecordingState == false) {
            isRecordingState = true

            myJob?.cancel()
            ...

        }
    }
}

错误理解纠正及问题原因

你的原有认知存在两个核心错误:

  1. init块并非在所有成员初始化完成后执行:Kotlin类的初始化流程是严格按照代码书写顺序执行的——从上到下依次执行成员变量的初始化逻辑,遇到init块就立即执行其内容,而非等所有成员都初始化完毕再统一执行init。
  2. 函数定义顺序确实不影响调用(比如beginSoundDensity在init之后定义仍能被调用),但函数执行时依赖的成员必须已经完成初始化,否则会触发未初始化相关的异常。

Code A触发NPE的具体原因

在Code A中,init块写在isRecordingState之前,初始化时会先执行init里的beginSoundDensity()调用。此时isRecordingState的委托对象(mutableStateOf返回的State实例)还未被创建——by关键字本质是委托,底层会生成对应的getValue/setValue方法,此时访问isRecordingState会尝试调用一个尚未初始化的委托对象的方法,直接触发NullPointerException。

Code B恢复正常的原因

Code B将init块移至所有成员变量定义之后,此时_timeXState、isRecordingState、myJob都已完成初始化,beginSoundDensity()调用时访问isRecordingState能正常获取到委托对象的值,因此不会触发异常。

内容的提问来源于stack exchange,提问作者HelloCW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:27:30