一杯 Kotlin 美式学透 enum class

enum class 是一种特殊的类,用来定义一组固定的相关常量。当你需要处理预定义选项时,比如一周中的日期、颜色或状态,枚举类就非常合适。

在 Kotlin 中,枚举类通过 enum 关键字加类名来声明。枚举中的每个常量都是一个单例实例,常量之间使用逗号分隔。

kotlin 复制代码
enum class Color {
    RED, GREEN, BLUE
}

在这个例子里,Color 是一个包含三个常量的枚举类:REDGREENBLUE

属性和方法

枚举常量不仅可以表示固定值,也可以带有属性和方法。做法是在主构造函数中定义属性,并让每个枚举常量携带对应的值。

下面是一个带属性和方法的示例:

kotlin 复制代码
enum class Direction(val degrees: Int) {
    NORTH(0),
    EAST(90),
    SOUTH(180),
    WEST(270);

    fun description(): String {
        return "Direction $name is $degrees degrees from North."
    }
}

这里,Direction 拥有一个 degrees 属性和一个 description() 方法,因此每个枚举常量都可以直接访问这些能力。

#\ 使用枚举常量

你可以在代码中直接使用枚举常量,也可以读取它们的属性、调用它们的方法。

kotlin 复制代码
val direction = Direction.NORTH
println(direction.description()) // 输出:Direction NORTH is 0 degrees from North.

when 表达式也是枚举类非常常见的搭配。由于编译器知道所有可能的枚举值(穷尽------非常好的特性),因此它会要求你把所有分支都处理完整:

kotlin 复制代码
fun handleDirection(direction: Direction): String {
    return when (direction) {
        Direction.NORTH -> "You are heading North."
        Direction.EAST -> "You are heading East."
        Direction.SOUTH -> "You are heading South."
        Direction.WEST -> "You are heading West."
    }
}

常用特性

Kotlin 为枚举类提供了一些内建的属性和方法:

  • name:返回枚举常量的名字,类型为 String
  • ordinal:返回枚举常量在声明中的位置,从 0 开始计数。
  • values():返回包含全部枚举常量的数组。
  • valueOf(String):根据名称返回对应的枚举常量。
kotlin 复制代码
val colors = Color.values()
println(colors.joinToString()) // 输出:RED, GREEN, BLUE

val color = Color.valueOf("RED")
println(color.ordinal) // 输出:0

使用场景

当一个变量必须从一组预定义值中取值时,枚举类通常就是理想选择:

  1. 表示状态,例如 loadingsuccesserror
  2. 定义固定选项,例如用户角色 ADMINEDITORVIEWER
  3. 配合 when 表达式简化控制流。

总结

枚举类为"固定且相关的一组值"提供了一种结构化表达方式。它既支持属性和方法,也能让代码在表达状态、选项或类别时更清晰。

使用枚举类后,代码通常会更具可读性,也更不容易在处理预定义值时出错。

进阶:泛型 values() 与 valueOf()

关于泛型 values()valueOf()KEEP 提案,为 Kotlin 枚举的使用方式带来了进一步优化。

当前 Kotlin 中的 values()valueOf() 都是静态方法,需要通过具体的枚举类来调用。这个提案希望把它们变成泛型方法,从而让用法更简洁,同时也更具类型安全。

主要变化包括:

  1. 泛型 values<T>() 方法: 该方法会返回某个指定枚举类型的全部常量,因此你不必显式写出具体枚举类,也能动态获取枚举值。这能有效减少样板代码。
  2. 泛型 valueOf<T>(String) 方法: 该方法允许根据名称动态获取某个枚举常量,同时继续保证类型安全。泛型方式也避免了在处理未知枚举类型时依赖反射方案。

优势

这个提案强调了几个明显好处:

  • 更强的类型安全: 泛型方法可以减少运行时错误,确保使用的是正确的枚举类型。
  • 更简洁的 API 设计: 在框架或库中处理枚举时,不必再为每个具体枚举写死引用。
  • 更好的可读性: 动态处理枚举时,可以显著减少冗长的样板代码。

向后兼容

这个提案的设计目标之一就是保持向后兼容。现有的 values()valueOf() 用法不会受到影响,泛型版本只是作为新增能力引入。

对反射与库的影响

泛型版本可以很好地融入现有 Kotlin 特性,使得在基于反射的库或 API 中处理枚举变得更方便。例如,一个需要处理各种枚举的库,可以在编译期不知道具体枚举类型的情况下,依然通过泛型方法完成操作。

总之,

泛型 values()valueOf() 的提案让枚举处理更简洁,也更灵活。它在减少样板代码的同时提升了类型安全,尤其适合那些需要以泛型方式处理枚举的框架和库。

进阶:弃用 values 改用 entries

关于枚举中 entries 属性的 KEEP 提案,引入了一个新的访问方式,用 List<E> 来返回全部枚举常量,而不是像 values() 那样返回数组。

为什么会有这个提案呢?

数组默认是可变的,如果要想一个枚举安全的返回 values(),那么每次调用 values() 方法都必须分配一个新的数组实例。这样就会有潜在的性能隐患,如果调用者盲目的调用多次,在内存上就会有极大的负担,而使用这个 API 的开发者,并不知道有这个潜在的性能负担(我相信很多开发者没有考虑到这个问题)。

改成一个不可变的 List------实际上是一个 List 的子类型 EnumEntries,这样不仅能提高代码可读性,也能避免反复创建数组,从而改善性能与内存效率。

entries 会与 values() 同时存在,以便兼容旧代码、平滑迁移。使用 entries 之后,访问枚举常量会更直接:

kotlin 复制代码
for (value in MyEnum.entries) {
    println(value)
}

这一增强为处理枚举提供了更现代、更高效的方式,同时又不会影响已有代码。

进阶:sealed class 和 enum class 怎么选

sealed class 更适合用来描述复杂的层级结构或状态系统,因为它的每个子类型都可以拥有各自独立的属性和行为。它非常适合那些数据结构会变化、或者需要通过多态来表达状态的场景,并且同样可以在 when 表达式中获得穷尽性检查。

相比之下,enum class 更适合表示一组固定常量,比如预定义类别或枚举值,整体结构和能力都更简单直接。

从子类型的灵活性来看,sealed class 可以定义多个不同形态的子类型,包括 objectdata class 和普通 class,每一种都可以拥有自己的构造参数、属性和方法,因此很适合在同一层级中表达差异较大的实体。

enum class 则局限于一组预先声明好的常量,每个常量都是同一种类型的实例,常量之间也不具备继承或多态关系。

简单来讲,如果你希望你的类有自己的行为和独立的属性,那么优先使用 sealed class

对于 sealed class,可以用它来表示不同的支付方式:

kotlin 复制代码
sealed class Payment {
    object Cash : Payment()
    data class CreditCard(val cardNumber: String) : Payment()
    data class PayPal(val email: String) : Payment()
}

fun processPayment(payment: Payment) = when (payment) {
    is Payment.Cash -> println("Paying with cash")
    is Payment.CreditCard -> println("Paying with card: ${payment.cardNumber}")
    is Payment.PayPal -> println("Paying via PayPal: ${payment.email}")
}

对于 enum class,则可以把它看成是对方位这样的固定集合建模:

kotlin 复制代码
enum class Direction(val degrees: Int) {
    NORTH(0),
    EAST(90),
    SOUTH(180),
    WEST(270);

    fun describe() = "Direction $name with $degrees degrees."
}

fun printDirection(direction: Direction) {
    println(direction.describe())
}

关键特性

sealed class 支持继承,因此能够承载更复杂的子类型行为和属性,更适合复杂场景。

enum class 虽然约束更多,但在表示固定值集合时更加轻量、高效,也可以带有共享行为和可选属性。

二者都支持 when 的穷尽性检查,保证编译期安全。

不过,如果每个分支都需要不同逻辑或额外数据,sealed class 往往更合适;如果只是表示预定义、固定不变的一组常量,enum class 通常就是更直接的选择。

使用场景

sealed class 常见于网络响应、UI 状态管理、有限状态机等场景,因为这些场景中的每一种状态往往都可能拥有不同的数据或行为。

enum class 更适合表示方向、一周中的日期、预定义状态等静态集合。

总之,

sealed class 更灵活,适合复杂层级和多态场景;enum class 更轻量,适合静态、固定值的枚举集合。最终选择哪一种,取决于你要表达的数据结构复杂度,以及是否需要为不同分支附加独立行为。

相关推荐
实时云渲染dlxyz66882 小时前
点盾云安卓版手机/平板播放器使用教程
android·点盾云播放·点盾云安卓使用·点盾云安卓播放器·加密播放
nix.gnehc2 小时前
OpenClaw 安卓设备接入指南:从零开始配置你的移动节点
android·openclaw
RDCJM12 小时前
【MySQL】在MySQL中STR_TO_DATE()以及其他用于日期和时间的转换
android·数据库·mysql
冬奇Lab13 小时前
AudioFlinger架构基础:Android音频系统的心脏
android·音视频开发·源码阅读
铁手飞鹰14 小时前
Visual Studio创建Cmake工程导出DLL,通过Python调用DLL
android·python·visual studio
冰语竹19 小时前
Android学习之相对布局
android
没有了遇见19 小时前
Android 中大型项目架构梳理
android
yashuk19 小时前
【MySQL】表的相关操作
android·mysql·adb
71-319 小时前
Android studio中真机操作
android·笔记·学习·其他·android studio