Kotlin enum val 返回 null,尽管它是在编译时设置的
Kotlin enum val is returning null, despite being set at compile time
我有一个包含多个字段的 Kotlin 枚举。其中一个字段 (enterableBoard
) 是另一个枚举的值。在运行时,尽管已设置此 val,但它仍为 null。
我以前从未见过这样的东西,其他字段工作正常,甚至是引用当前 Item
的字段!我已经包括了完整的枚举定义,因为我不确定什么是相关的。
Item
枚举和一个值,其中 enterableBoard
意外为空而不是 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)
}
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))
}
这是 enterableBoard
意外为空的屏幕截图:
补充说明:
- 我试过更改
TREE_9
中的其他值,它们都更新了,所以包含的定义肯定是正在使用的定义。
- 尝试在代码中访问该字段时,该字段也为空。
- Kotlin 版本 1.5.20.
- Android Studio 2020.3.1 补丁 1.
我很确定这是因为您有相互参考。 Board 在其构造函数中引用 Item,反之亦然。两个枚举 类' 成员中的一个在另一个之前实例化,因此对其他值的引用在引用它们时仍然为空。这是一种偷偷摸摸的方法,你可以通过做一些你不应该做的事情,比如调用来自构造函数的 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)
}
打印null
是因为引用Item.X
导致Item的成员先被尝试实例化,但是由于它的构造函数引用了Board,所以Board实际上是先实例化的,并且使用仍然为null的Item.X
参考。
我有一个包含多个字段的 Kotlin 枚举。其中一个字段 (enterableBoard
) 是另一个枚举的值。在运行时,尽管已设置此 val,但它仍为 null。
我以前从未见过这样的东西,其他字段工作正常,甚至是引用当前 Item
的字段!我已经包括了完整的枚举定义,因为我不确定什么是相关的。
Item
枚举和一个值,其中 enterableBoard
意外为空而不是 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)
}
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))
}
这是 enterableBoard
意外为空的屏幕截图:
补充说明:
- 我试过更改
TREE_9
中的其他值,它们都更新了,所以包含的定义肯定是正在使用的定义。 - 尝试在代码中访问该字段时,该字段也为空。
- Kotlin 版本 1.5.20.
- Android Studio 2020.3.1 补丁 1.
我很确定这是因为您有相互参考。 Board 在其构造函数中引用 Item,反之亦然。两个枚举 类' 成员中的一个在另一个之前实例化,因此对其他值的引用在引用它们时仍然为空。这是一种偷偷摸摸的方法,你可以通过做一些你不应该做的事情,比如调用来自构造函数的 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)
}
打印null
是因为引用Item.X
导致Item的成员先被尝试实例化,但是由于它的构造函数引用了Board,所以Board实际上是先实例化的,并且使用仍然为null的Item.X
参考。