You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala 3如何使用宏将字符串集合转换为对应case的enum枚举类型

结论

可以实现,但你原本的写法存在3个核心问题需要调整:

  1. Scala 3 宏仅能处理编译期可见的常量值,如果字符串序列是运行时传入Example构造器的,宏在编译阶段无法读取到具体值,也就没法生成对应enum case。
  2. 你想要的「每个实例绑定独立enum类型」属于路径依赖类型需求,但enum定义只能出现在静态作用域、inline方法块或宏生成的代码块中,不能绑定到普通类的运行时实例上。
  3. 跨文件使用生成的枚举,需要把生成逻辑放在public inline方法中,确保依赖的代码在编译时能展开生成对应的enum定义。

可行实现方案

第一步:编写编译期宏逻辑

需要把宏代码放在单独的编译期模块,或者项目的Compile / compile-time源目录下:

import scala.quoted.*

inline def buildEnumHolder(inline cases: List[String]): Any = ${ buildImpl('cases) }

private def buildImpl(casesExpr: Expr[List[String]])(using Quotes): Expr[Any] =
  import quotes.reflect.*
  // 校验输入为编译期常量字符串
  val caseNames = casesExpr.valueOrAbort

  // 生成枚举case定义
  val enumCases = caseNames.map(name =>
    EnumCase(
      name = name,
      parents = List(TypeTree.of[Enum]),
      args = Nil
    )
  )

  // 生成枚举类型定义
  val enumDef = EnumDef(
    name = "MyEnum",
    parents = List(TypeTree.of[java.lang.Enum[?]]),
    cases = enumCases,
    body = Nil
  )

  // 生成包含枚举和快捷访问方法的单例对象
  val holderDef = ClassDef(
    name = "EnumHolder",
    parents = List(TypeTree.of[Object]),
    body = List(enumDef) ++ caseNames.map(name =>
      DefDef(
        name = name,
        paramss = Nil,
        returnTpt = TypeTree.ref(enumDef.symbol),
        rhs = Some(Ref(enumDef.symbol.declaredType(name).termSymbol))
      )
    )
  )

  // 构造单例对象并返回
  Block(
    List(holderDef),
    New(TypeTree.ref(holderDef.symbol)).select(holderDef.symbol.primaryConstructor).appliedToNone
  ).asExprOf[Any]

第二步:业务代码调用

// 必须用inline val声明,确保是编译期常量
inline val enumCases = List("A", "B", "C")
// 用结构类型接收返回值,asInstanceOf是安全的,宏生成的结构完全匹配
val holder = buildEnumHolder(enumCases).asInstanceOf[{
  type MyEnum
  def A: MyEnum
  def B: MyEnum
  def C: MyEnum
}]

// 正常使用枚举
def someConvenienceFunction(e: holder.MyEnum) = e match
  case holder.A => "apple"
  case holder.B => "banana"
  case holder.C => "cranberry"

// 编译通过,返回"apple"
someConvenienceFunction(holder.A)
// 编译报错:value D is not a member of 结构类型
someConvenienceFunction(holder.D)

补充说明

  • 如果输入字符串不是编译期常量(比如从配置文件读取的运行时值),无法实现编译期校验不存在的枚举项,只能用sealed trait + case对象模拟枚举,在调用点做运行时校验。
  • 跨文件使用生成的枚举时,只需要保证传入的case列表是inline常量,宏会在每个依赖的编译单元自动展开生成对应定义。
  • 可以根据需求在宏生成阶段给枚举补充valueOf、values、自定义属性等额外方法。

内容的提问来源于stack exchange,提问作者pnips

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 03:45:04