尽管在编译时设置,Kotlin enum val 仍返回 null

Jak*_*eam 1 enums android kotlin

我有一个包含多个字段的 Kotlin 枚举。这些字段之一 ( enterableBoard) 是另一个枚举的值。在运行时,尽管已设置,但该 val 仍为 null。

我以前从未见过这样的事情,其他字段工作正常,即使是引用 current 的字段Item!我已经包含了完整的枚举定义,因为我不确定什么是相关的。

Item枚举和一个值,其中enterableBoard意外的是 null 而不是Board.SPACEX_EARLY_TREES

enum class Item(
    val chain: ItemChain,
    val tier: Int,
    @StringRes val title: Int,
    @DrawableRes val image: Int,
    val generatorMaxEnergy: Int = 0, 
    val enterableBoard: Board? = null,
    val mergeBonus: Pair<Item, Int>? = null, 
    val redeemable: Pair<InfoLong, Int>? = null
) {
    TREE_9(ItemChain.TREE, 9, R.string.item_tree9, R.drawable.item_tree9,
        generatorMaxEnergy = 50, enterableBoard = Board.SPACEX_EARLY_TREES)
}
Run Code Online (Sandbox Code Playgroud)

Board枚举和相关值:

enum class Board(
    val campaign: Campaign,
    @StringRes val title: Int,
    @StringRes val description: Int,
    @DrawableRes val background: Int,
    @DrawableRes val image: Int,
    val unlockCost: Int,
    val template: List<Item>
) {
    SPACEX_EARLY_TREES(Campaign.SPACEX_EARLY, R.string.board_spacex_early_tree_title, R.string.board_spacex_early_tree_description,
        R.drawable.background5, R.drawable.background5, 0,
        listOf(Item.FRUITVEG_1, Item.FRUITVEG_2, Item.FRUITVEG_1))
}
Run Code Online (Sandbox Code Playgroud)

enterableBoard这是意外为空的屏幕截图:

android studio 评估屏幕

补充笔记:

  • 我尝试更改 中的其他值TREE_9,它们都会更新,因此包含的定义肯定是正在使用的定义。
  • 当尝试在代码中访问该字段时,该字段也为空。
  • Kotlin 版本 1.5.20。
  • Android Studio 2020.3.1 补丁 1。

Ten*_*r04 5

我很确定这是因为你们有一个相互参考。Board 在其构造函数中引用 Item,反之亦然。两个枚举类的成员之一在另一个之前实例化,因此对其他值的引用在引用它们时仍然为空。这是在 Kotlin 中获得对 null 的不可为空引用的偷偷摸摸的方法之一(如果你的Board参数不可为空,这种情况仍然会发生),通过做一些你不应该做的事情,比如调用open从构造函数调用函数。

基本上,枚举不能安全地相互引用,因为它们在实例化时的编译和行为方式不同。编译器是否应该针对这种情况显示警告或错误?可能吧,但显然不是。

这是该问题的最小可重现示例:

enum class Item (val board: Board) {
    X(Board.Y)
}

enum class Board(val item: Item) {
    Y(Item.X)
}

fun main() {
    Item.X
    println(Board.Y.item)
}
Run Code Online (Sandbox Code Playgroud)

打印是null因为对的引用Item.X导致 Item 的成员首先尝试实例化,但由于其构造函数引用 Board,因此 Board 实际上首先实例化并使用仍然为空的Item.X引用。