Kotlin:如何确保枚举全组合有返回值且避免嵌套when?
解决方案
针对多枚举组合的价格映射需求,既要保证所有组合被覆盖的校验,又避免嵌套when的繁琐,有两种实用方案可选:
方案1:运行时全覆盖校验的HashMap映射
用数据类封装套餐组合,配合HashMap存储价格,同时在初始化时遍历所有枚举的笛卡尔积,强制校验每个组合都有对应的价格。这种方式代码简洁,开发阶段就能快速发现遗漏,无需手写大量分支。
enum class Meat { Ham, Bacon, Steak } enum class Drink { Milk, OJ, Coffee, Tea } enum class Eggs { Scrambled, Fried, OverEasy } data class ComboMeal(val meat: Meat, val drink: Drink, val eggs: Eggs) // 定义所有套餐的价格映射 private val comboPriceMap: Map<ComboMeal, Double> = mapOf( ComboMeal(Meat.Ham, Drink.Milk, Eggs.Scrambled) to 10.77, ComboMeal(Meat.Ham, Drink.Milk, Eggs.Fried) to 11.22, // ... 补充所有其他组合的价格 ) // 初始化时校验所有组合是否都有价格 init { // 生成所有可能的套餐组合(枚举笛卡尔积) val allPossibleCombos = Meat.values().flatMap { meat -> Drink.values().flatMap { drink -> Eggs.values().map { eggs -> ComboMeal(meat, drink, eggs) } } } // 找出未定义价格的组合 val missingCombos = allPossibleCombos.filterNot { comboPriceMap.containsKey(it) } // 如果有遗漏,抛出异常终止启动 require(missingCombos.isEmpty()) { "未定义价格的套餐组合:$missingCombos" } } fun getComboPrice(meat: Meat, drink: Drink, eggs: Eggs): Double { // 直接从映射中取值,由于初始化时已校验,不会出现KeyNotFound return comboPriceMap.getValue(ComboMeal(meat, drink, eggs)) }
优点
- 代码结构清晰,无需维护大量
when分支 - 开发阶段启动时就会报错,避免运行时出现未定义价格的情况
- 枚举新增值时,初始化校验会自动检测到遗漏的组合
缺点
- 校验发生在运行时而非编译期,但开发阶段的启动校验已经足够提前发现问题
方案2:代码生成自动生成穷尽式when分支
利用Kotlin的枚举遍历能力,编写代码生成工具(比如用KotlinPoet)自动生成所有组合的when分支,让编译器直接做穷尽性检查。这种方式能获得编译期的严格校验,无运行时开销。
核心思路
- 遍历所有枚举的
values(),生成所有可能的笛卡尔积组合 - 自动生成包含所有组合分支的
getComboPrice函数代码 - 生成的代码会让编译器强制检查是否覆盖所有组合,遗漏时直接编译报错
生成后的代码示例:
fun getComboPrice(meat: Meat, drink: Drink, eggs: Eggs): Double { return when (Triple(meat, drink, eggs)) { Triple(Meat.Ham, Drink.Milk, Eggs.Scrambled) -> 10.77 Triple(Meat.Ham, Drink.Milk, Eggs.Fried) -> 11.22 Triple(Meat.Ham, Drink.Milk, Eggs.OverEasy) -> 11.55 // ... 自动生成所有组合的分支 } }
优点
- 编译期严格校验所有组合是否被覆盖,无运行时风险
- 无需手动编写大量重复的
when分支
缺点
- 需要维护代码生成工具,适合枚举结构稳定、不频繁变动的场景
方案选择建议
- 如果枚举可能频繁新增或修改,优先选方案1,维护成本更低
- 如果需要最严格的编译期校验,且枚举结构稳定,优先选方案2
内容的提问来源于stack exchange,提问作者Tim Reistetter
相关产品推荐
相关产品推荐

