Kotlin中能否定义枚举类子集?如何实现when语句穷尽性检查
问题描述
给定如下Kotlin枚举类及扩展函数:
enum class Shape { FULL_HEIGHT_WALL, HALF_HEIGHT_WALL, FULL_HEIGHT_CORNER, HALF_HEIGHT_CORNER, SOMETHING_ELSE } fun Shape.isFullHeight() = when (this) { Shape.FULL_HEIGHT_WALL -> true Shape.FULL_HEIGHT_CORNER -> true else -> false } fun Shape.isHalfHeight() = when (this) { Shape.HALF_HEIGHT_WALL -> true Shape.HALF_HEIGHT_CORNER -> true else -> false } fun test(s: Shape) = when { s.isFullHeight() -> 1.0f s.isHalfHeight() -> 0.5f s == Shape.SOMETHING_ELSE -> 0.42f // else?! }
其中isFullHeight、isHalfHeight分别对应Shape的两个子集,test函数的when语句理论上已覆盖所有枚举值,但Kotlin无法识别这些检查的穷尽性,要求添加else分支。然而添加else后,若新增枚举值且未更新test函数,编译器不会报错。
请问:
- 是否可以改写代码让Kotlin识别这类子集检查的穷尽性?
- 如果不行,该特性是否在Kotlin的规划路线中?(注:TypeScript支持此特性)
问题解答
一、当前可实现的改写方案
Kotlin编译器目前没法直接识别基于扩展函数的子集检查是否穷尽,但可以通过两种方式改写,既保证编译器能检测穷尽性,又保留子集逻辑:
1. 直接在when中枚举分组
放弃扩展函数,把同属一个子集的枚举值放在when的同一个分支里,编译器会自动检查是否覆盖所有枚举项:
fun test(s: Shape) = when(s) { Shape.FULL_HEIGHT_WALL, Shape.FULL_HEIGHT_CORNER -> 1.0f Shape.HALF_HEIGHT_WALL, Shape.HALF_HEIGHT_CORNER -> 0.5f Shape.SOMETHING_ELSE -> 0.42f }
优点是新增枚举值时编译器会立刻报错,提醒你更新test函数;缺点是全高/半高的判断逻辑无法复用,其他地方需要时得重复写枚举值列表。
2. 用密封类封装子集分类
如果需要复用全高/半高的判断逻辑,可以用密封类定义分类,让枚举类关联对应的分类:
sealed interface HeightType { object Full : HeightType object Half : HeightType object Other : HeightType } enum class Shape(val heightType: HeightType) { FULL_HEIGHT_WALL(HeightType.Full), HALF_HEIGHT_WALL(HeightType.Half), FULL_HEIGHT_CORNER(HeightType.Full), HALF_HEIGHT_CORNER(HeightType.Half), SOMETHING_ELSE(HeightType.Other) } fun test(s: Shape) = when(s.heightType) { HeightType.Full -> 1.0f HeightType.Half -> 0.5f HeightType.Other -> 0.42f }
这种写法既可以通过shape.heightType在其他地方复用分类逻辑,又能让编译器检测穷尽性——新增枚举值时必须指定对应的HeightType,否则编译不通过;修改HeightType的子类时,test函数的when也会提示未覆盖。
二、关于特性规划
目前Kotlin官方没有公开计划支持"通过函数调用识别枚举子集穷尽性"的特性。TypeScript能实现该特性是因为其结构类型系统和控制流分析逻辑与Kotlin不同,Kotlin的标称类型系统要实现类似功能需要更复杂的数据流分析,暂时没有相关开发计划。
内容的提问来源于stack exchange,提问作者digory doo
相关产品推荐
相关产品推荐

