枚举类:枚举常量、枚举属性与方法、枚举实现接口、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 枚举类知识体系 enum class 枚举常量 类型安全,替代魔法值 属性与方法 构造函数、成员函数、抽象方法 实现接口 常量可自定义行为 when 表达式 穷举检查,类型安全 内置方法 name, ordinal, entries 等 高级用法 抽象方法、策略模式 枚举 = 类型安全 + 行为封装 + 可扩展

总结

枚举在 Kotlin 里远不止是常量列表。它可以有属性、方法,可以实现接口,还能和 when 完美配合。我个人觉得,用好枚举能让代码更安全、更易读。尤其是状态管理、策略模式、错误码这些场景,枚举几乎是首选。

嗯,最后提醒一句:如果你发现自己在写一堆 if-else 判断常量,不妨想想能不能用枚举 + when 重构一下。代码会清爽很多。

核心要点:
  • 枚举常量是类型安全的,避免魔法值
  • 枚举可以带属性和方法,甚至抽象方法
  • 枚举可以实现接口,每个常量行为可以不同
  • when 表达式能穷举枚举,编译器帮你检查完整性
  • 优先使用 entries 而不是 values()

公众号:蓝海资料掘金营,微信deep3321