1. 概述

在 Kotlin 中,enum 是定义一组固定值的常用方式。它非常适合表示一组相关的常量,代码清晰且类型安全。

但在实际开发中,我们常常需要以编程方式获取某个枚举类中所有条目的名称(即 name 字段),例如用于日志打印、参数校验、前端下拉选项生成等场景。

本文将介绍几种使用泛型函数实现该需求的方法,重点对比其适用性与优缺点,帮你避开一些常见的“坑”。


2. 问题背景

为了支持任意枚举类型,我们需要一个通用的、可复用的函数,能够接受任意 Enum<T> 类型并返回其所有条目的名称列表。

先定义两个示例枚举类,便于后续验证:

enum class CountryCode(countryName: String) {
    USA("United States of America"),
    UKR("Ukraine"),
    CAN("Canada"),
    MEX("Mexico"),
    JAM("Jamaica")
}

enum class WorkingDay {
    Monday, Tuesday, Wednesday, Thursday, Friday
}

✅ 注意:CountryCode 带有构造参数和属性,而 WorkingDay 是最简单的枚举形式。我们的目标是写出一个能同时处理这两种情况的泛型函数。

最终期望效果如下:

getNames<WorkingDay>() // 返回 ["Monday", "Tuesday", ..., "Friday"]
getNames<CountryCode>() // 返回 ["USA", "UKR", "CAN", "MEX", "JAM"]

下面我们来看几种主流实现方式。


3. 使用 Java 的 Class.getEnumConstants() 方法

Java 的 java.lang.Class 提供了 getEnumConstants() 方法,可以返回枚举类的所有实例数组。

在 Kotlin 中也可以直接调用这个方法,步骤如下:

  1. 获取枚举类的 Java Class 对象(通过 ::class.java
  2. 调用 enumConstants 属性获取实例数组
  3. 使用 map { it.name } 提取名称

示例代码:

val names = WorkingDay::class.java.enumConstants.map { it.name }
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), names)

将其封装为泛型函数:

fun getNames1(enumCls: Class<out Enum<*>>) = enumCls.enumConstants.map(Enum<*>::name)

⚠️ 注意点:

  • 参数必须是 Class<out Enum<*>>,确保传入的是枚举类的 Class 对象
  • 调用时需显式传递 ::class.java,语法略显冗长

测试验证:

val dayNames = getNames1(WorkingDay::class.java)
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = getNames1(CountryCode::class.java)
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

✅ 优点:兼容性强,适用于所有 Kotlin 版本
❌ 缺点:调用不够简洁,依赖 Java API,风格不够“Kotlin”


4. 使用 Kotlin 的 enumValues<T>() 函数

Kotlin 标准库提供了 enumValues<T>() 函数,专门用于获取指定枚举类型的所有条目:

inline fun <reified T : Enum<T>> enumValues(): Array<T>

关键特性:

  • reified 类型参数允许运行时获取泛型信息
  • inline 函数避免额外开销
  • ✅ 纯 Kotlin 实现,无需依赖 Java Class API

基于此,我们可以写出更简洁的泛型函数:

inline fun <reified T : Enum<T>> getNames2() = enumValues<T>().map { it.name }

调用方式非常直观:

val dayNames = getNames2<WorkingDay>()
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = getNames2<CountryCode>()
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

✅ 优点:

  • 语法简洁,符合 Kotlin 风格
  • 性能良好(内联 + reified)
  • 不依赖 Java API

❌ 缺点:

  • 必须使用 <T> 显式指定类型,不能自动推导
  • 在某些高阶用法中可能受限于类型擦除(但 reified 已解决大部分问题)

📌 推荐指数:⭐⭐⭐⭐☆(适合大多数场景)


5. 使用 values() 函数和 entries 属性

虽然前面两种方式都能工作,但调用形式仍是“函数式”的,比如 getNames2<WorkingDay>()。而在实际编码中,我们更希望写成类似 WorkingDay.values().names() 这样的链式调用,更具可读性和流畅性。

5.1 扩展 values() 数组 —— 添加 .names() 扩展函数

Kotlin 允许为数组添加扩展函数。我们可以为所有 Array<out Enum<T>> 类型添加一个 names() 扩展:

fun <T : Enum<T>> Array<T>.names() = this.map { it.name }

这样就可以像下面这样调用:

val dayNames = WorkingDay.values().names()
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = CountryCode.values().names()
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

✅ 优点:

  • 调用极其自然,符合 Kotlin DSL 风格
  • 复用现有 values() 方法,无额外性能损耗
  • 可与其他集合操作无缝衔接(如过滤、排序等)

⚠️ 注意:values() 每次调用都会创建新数组(除非 JVM 优化),频繁调用可能影响性能。


5.2 使用 entries 属性(Kotlin 1.9+ 推荐)

从 Kotlin 1.8.20 开始,JetBrains 引入了实验性的 entries 属性作为 values() 的现代替代方案。自 Kotlin 1.9.0 起已稳定可用

🔗 官方说明:https://kotlinlang.org/docs/whatsnew1820.html#a-modern-and-performant-replacement-of-the-enum-class-values-function

主要优势:

  • entries 返回的是只读 List,而非每次新建的数组
  • ✅ 更节省内存,更适合高频访问场景
  • ✅ 支持序列化友好访问(尤其与 kotlinx.serialization 配合更好)

我们可以为 EnumEntries<T> 添加类似的扩展:

fun <T : Enum<T>> EnumEntries<T>.names() = this.map { it.name }

使用方式几乎一致:

val dayNames = WorkingDay.entries.names()
assertEquals(listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), dayNames)

val codeNames = CountryCode.entries.names()
assertEquals(listOf("USA", "UKR", "CAN", "MEX", "JAM"), codeNames)

✅ 强烈建议升级到 Kotlin 1.9+ 的项目优先使用 entries 替代 values()

📌 对比总结:

方式 返回类型 是否每次新建 推荐程度
values() Array<T> ⭐⭐⭐
entries List<T>(共享) ⭐⭐⭐⭐⭐

6. 结论

本文系统介绍了四种获取枚举条目名称的泛型方法,各有适用场景:

方法 适用版本 推荐度 说明
Class.getEnumConstants() 所有版本 ⭐⭐ 兼容老项目,但偏 Java 风格
enumValues<T>() 所有版本 ⭐⭐⭐⭐ Kotlin 原生推荐,简洁高效
values().names() 扩展 所有版本 ⭐⭐⭐⭐ 流畅调用,适合 DSL 风格
entries.names() 扩展 Kotlin 1.9+ 首选 最佳实践,性能与语义俱佳

📌 最终建议

  • 若使用 Kotlin ≥ 1.9,优先采用 entries + 扩展函数的方式
  • 否则使用 enumValues<T>() 封装即可
  • 避免在高频路径中反复调用 values(),防止不必要的对象创建

所有示例代码均可在 GitHub 查看:
https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-lang-oop-3


原始标题:Get Names of All Enum Entries by Generic Functions in Kotlin