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

Kotlin密封类对象初始化顺序为何异常?如何规避NPE?

问题分析与解决方案

你违反的规则

你违反了Kotlin嵌套object与companion object的初始化顺序规则:

  1. Kotlin的嵌套object(非companion)是懒加载模式,只有首次被直接或间接引用时才会完成初始化。
  2. companion object的初始化时机是其外部类首次被访问(包括访问嵌套object、调用companion方法等),但如果在companion初始化前提前访问某个嵌套object,会导致初始化顺序交叉,使companion中引用的其他未被访问的object在companion初始化时还未完成实例化,最终在values列表中留下null。

具体到你的代码:当你先访问Animal.Fox时,JVM会触发Fox的初始化流程,但此时Bear、Deer和companion object都未初始化。后续访问Animal.values时触发companion初始化,构建listOf(Bear,Fox,Deer)时会依次触发Bear、Deer的初始化,但Fox的初始化流程因父类与companion的初始化交叉被打断,导致list中的Fox位置暂时为null——即便后续Fox完成初始化,列表里的null也不会自动更新,最终引发NPE。

解决方法

方法1:用lazy延迟初始化values

将values改为延迟初始化,确保它第一次被访问时才构建列表,此时所有嵌套object都会被强制初始化,彻底避免顺序问题:

sealed class Animal(val key: String) {
    object Bear : Animal("bear")
    object Fox : Animal("fox")
    object Deer : Animal("deer")

    init {
        println("Animal::init. key : $key")
    }

    companion object {
        // 首次访问values时才构建列表,强制所有object完成初始化
        val values by lazy { listOf(Bear, Fox, Deer) }

        init {
            println("Animal.Companion::init")
        }

        fun valueOf(key: String) = values.firstOrNull { key == it.key }
    }
}

方法2:在companion初始化时主动触发所有object初始化

在companion的init块中主动访问所有嵌套object,强制它们在values初始化前完成实例化:

sealed class Animal(val key: String) {
    object Bear : Animal("bear")
    object Fox : Animal("fox")
    object Deer : Animal("deer")

    init {
        println("Animal::init. key : $key")
    }

    companion object {
        val values = listOf(Bear, Fox, Deer)

        init {
            // 主动触发所有嵌套object的初始化,确保values无null
            Bear
            Fox
            Deer
            println("Animal.Companion::init")
        }

        fun valueOf(key: String) = values.firstOrNull { key == it.key }
    }
}

方法3:改用enum class(场景允许时)

如果你的密封类只是用来替代枚举的简单场景,直接使用Kotlin原生enum class是最安全的选择——它会自动处理所有实例的初始化顺序,原生支持entries获取所有实例,无需手动维护values列表:

enum class Animal(val key: String) {
    Bear("bear"),
    Fox("fox"),
    Deer("deer");

    init {
        println("Animal::init. key : $key")
    }

    companion object {
        fun valueOf(key: String) = entries.firstOrNull { it.key == key }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:14:52