如何用Scala 3宏在编译时为@entity注解类生成私有无参构造方法
用Scala 3宏为@entity注解类生成私有无参构造方法
需求说明
我们需要实现一个自定义注解@entity,当类被该注解标注时,编译阶段自动生成一个私有的无参构造方法,这个构造方法会调用类的主构造方法,并为每个参数传入对应类型的默认值(如Long用0,String用null)。
1. 定义@entity注解
首先定义注解类,继承StaticAnnotation并指定宏处理入口:
import scala.annotation.StaticAnnotation import scala.language.experimental.macros class entity extends StaticAnnotation { def macroTransform(using scala.quoted.Quotes): scala.quoted.Expr[Any] = EntityMacro.impl }
2. 实现Scala 3宏注解处理器
通过Quoted API操作编译期语法树,生成无参构造方法并合并到原类:
import scala.quoted.* object EntityMacro { def impl(using Quotes): Expr[Any] = { import quotes.reflect.* // 获取被注解的类定义 val classDef = Position.ofMacroExpansion.owner.asClassDef val clsName = classDef.name // 获取主构造方法 val primaryCtor = classDef.primaryConstructor.getOrElse( report.errorAndAbort(s"类 ${clsName} 必须包含主构造方法") ) val ctorParams = primaryCtor.paramss.flatten // 生成参数默认值表达式 val defaultArgs = ctorParams.map { param => param.tpe.asType match { case '[Long] => Expr(0L) case '[String] => Expr(null: String) // 可扩展支持更多类型的默认值 case t => report.errorAndAbort(s"暂不支持类型 ${t.show} 的默认值生成") } } // 生成私有无参构造方法语法树 val noArgCtorDef = DefDef( name = "<init>", tparamss = Nil, paramss = Nil, returnTpt = TypeTree.of[Unit], rhs = Apply(Select(This(clsName), primaryCtor.name), defaultArgs.map(_.asTerm)) ).withFlags(Flags.Private) // 将新构造方法添加到原类 val newClassDef = classDef.copy(body = classDef.body :+ noArgCtorDef) // 转换为Expr返回 Expr(newClassDef).asExprOf[Any] } }
3. 使用示例
给目标类标注@entity,编译时自动生成私有无参构造方法:
@entity class Student(val id: Long, val name: String)
编译后等价于:
class Student(val id: Long, val name: String) { private def this() = this(0L, null) }
注意事项
- 宏当前仅支持
Long和String类型,如需扩展其他类型,在defaultArgs的匹配逻辑中添加对应分支即可。 - 被注解类必须包含主构造方法,否则会触发编译错误。
- 确保项目使用Scala 3.3+版本,原生支持宏特性,无需额外插件。
内容的提问来源于stack exchange,提问作者Golden
相关产品推荐
相关产品推荐

