枚举类:枚举常量、枚举属性与方法、枚举实现接口、when与枚举
枚举,说白了就是一组固定的常量。Java 里也有,但 Kotlin 的枚举玩出了新花样。我个人觉得,Kotlin 的枚举更像是一个「小型的类家族」——每个枚举常量都可以有自己的属性、方法,甚至还能实现接口。嗯,这就有意思了。
枚举常量:不只是名字
先看最基础的写法。你定义一个枚举,每个常量就是一个对象。但 Kotlin 里,它们不仅仅是字符串或者数字。
enum class Color {
RED, GREEN, BLUE
}
就这么简单?对。但如果你以为枚举只能干这个,那就太小看它了。我在项目中经常用枚举来替代那些「魔法数字」或者「硬编码字符串」。比如状态机里的状态:
enum class OrderStatus {
PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}
每个常量都是 OrderStatus 的一个实例。你可以直接比较:
if (currentStatus == OrderStatus.SHIPPED) {
// 发货了,发通知
}
你想想看,如果用字符串或者 Int,万一拼写错了,编译器可不会帮你。枚举就不一样,写错了直接编译报错。这就是类型安全。
枚举属性与方法:给常量加点料
枚举常量可以有自己的属性。比如颜色枚举,我想让每个颜色带一个 RGB 值:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
这里 rgb 是构造函数参数,每个常量在声明时传进去。调用的时候:
println(Color.RED.rgb) // 输出 16711680
还可以定义方法。注意,方法可以定义在枚举类内部,所有常量共享:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF);
fun hexString(): String {
return "#%06X".format(rgb)
}
}
调用:
println(Color.RED.hexString()) // 输出 #FF0000
我曾经在项目中用枚举来管理 API 的错误码。每个错误码带一个消息模板和一个严重级别。这样所有错误码都集中在一个地方,维护起来特别方便。
枚举实现接口:让常量更灵活
枚举可以实现接口。这意味着每个枚举常量都可以有不同的行为。举个例子,假设我们有一个 Runnable 接口:
interface Runnable {
fun run()
}
enum class Task : Runnable {
A {
override fun run() {
println("Task A is running")
}
},
B {
override fun run() {
println("Task B is running")
}
}
}
调用:
Task.A.run() // 输出 Task A is running
每个常量都实现了 run() 方法,但实现可以完全不同。这比用 when 分支更优雅。我记得有一次重构一个支付模块,每种支付方式(支付宝、微信、银联)都有不同的校验逻辑。用枚举实现接口,每种支付方式自己实现校验方法,代码清晰多了。
当然,你也可以让枚举类整体实现接口,然后所有常量共享同一个实现:
enum class Status : Runnable {
PENDING, CONFIRMED, SHIPPED;
override fun run() {
println("Status is $this")
}
}
这样所有常量都调用同一个 run() 方法。具体用哪种,看你的业务需求。
when 与枚举:天生一对
Kotlin 的 when 表达式和枚举是绝配。因为 when 可以穷举所有枚举常量,编译器会帮你检查是否覆盖完整。如果不完整,编译会报错(如果用了 else 分支则不会)。
fun describe(color: Color): String {
return when (color) {
Color.RED -> "热情"
Color.GREEN -> "自然"
Color.BLUE -> "冷静"
}
}
这里没有写 else,因为三个常量都覆盖了。如果以后新增一个 YELLOW,编译器会报错,提醒你修改 when 表达式。这比 Java 的 switch 安全多了。
你还可以在 when 里直接使用枚举的属性:
fun rgbDescription(color: Color): String {
return when (color.rgb) {
0xFF0000 -> "红色"
0x00FF00 -> "绿色"
0x0000FF -> "蓝色"
else -> "未知"
}
}
我个人习惯在状态机里大量使用 when + 枚举。比如订单状态流转:
fun nextStatus(current: OrderStatus): OrderStatus {
return when (current) {
OrderStatus.PENDING -> OrderStatus.CONFIRMED
OrderStatus.CONFIRMED -> OrderStatus.SHIPPED
OrderStatus.SHIPPED -> OrderStatus.DELIVERED
OrderStatus.DELIVERED -> OrderStatus.DELIVERED // 终点
OrderStatus.CANCELLED -> OrderStatus.CANCELLED // 终点
}
}
这样写,逻辑一目了然。而且如果以后新增一个状态,编译器会提醒你更新 when 分支。
when 处理枚举,但忘了加 else 分支,结果新增枚举常量后编译没报错(因为用了 else),导致运行时走了默认逻辑,出了 bug。所以,如果你希望编译器强制检查所有分支,就不要写 else。
枚举的高级用法:抽象方法与自定义行为
枚举可以定义抽象方法,每个常量自己实现。这比实现接口更直接:
enum class Operation {
PLUS {
override fun apply(x: Int, y: Int): Int = x + y
},
MINUS {
override fun apply(x: Int, y: Int): Int = x - y
},
TIMES {
override fun apply(x: Int, y: Int): Int = x * y
};
abstract fun apply(x: Int, y: Int): Int
}
调用:
println(Operation.PLUS.apply(3, 4)) // 7
每个常量都实现了 apply 方法。这种模式在策略模式里特别好用。你想想看,不用写一堆 if-else,直接枚举搞定。
枚举的常用内置方法
Kotlin 的枚举自带一些实用方法:
| 方法 | 说明 | 示例 |
|---|---|---|
name |
返回常量名称(字符串) | Color.RED.name → "RED" |
ordinal |
返回常量序号(从0开始) | Color.RED.ordinal → 0 |
values() |
返回所有常量数组 | Color.values() → [RED, GREEN, BLUE] |
valueOf() |
根据名称获取常量 | Color.valueOf("RED") → Color.RED |
entries |
Kotlin 1.9+ 推荐替代 values() | Color.entries → 枚举常量列表 |
我个人建议用 entries 而不是 values(),因为 entries 返回的是不可变列表,更安全,性能也更好。
知识体系图
下面这张图总结了枚举类的核心知识点和它们之间的关系:
总结
枚举在 Kotlin 里远不止是常量列表。它可以有属性、方法,可以实现接口,还能和 when 完美配合。我个人觉得,用好枚举能让代码更安全、更易读。尤其是状态管理、策略模式、错误码这些场景,枚举几乎是首选。
嗯,最后提醒一句:如果你发现自己在写一堆 if-else 判断常量,不妨想想能不能用枚举 + when 重构一下。代码会清爽很多。
- 枚举常量是类型安全的,避免魔法值
- 枚举可以带属性和方法,甚至抽象方法
- 枚举可以实现接口,每个常量行为可以不同
- when 表达式能穷举枚举,编译器帮你检查完整性
- 优先使用 entries 而不是 values()