如何获取Scala中StaticAnnotation成员的求值后的值?
如何获取Scala中StaticAnnotation成员的求值后的值?
这个问题我之前也踩过坑!你遇到的情况其实是Scala StaticAnnotation的特性导致的:默认情况下,Scala的静态注解参数在编译期只会保留符号引用,不会把变量求值后的实际值写入类文件的元数据,所以用常规的Scala反射自然拿不到"Test"这个结果。
咱们先理清楚核心问题:
- 你用
@FakeAnnotation(someVar)的时候,someVar在注解里是作为一个符号存在的,而不是直接替换成它的取值"Test" - Java注解之所以能拿到值,是因为Java要求注解参数必须是编译期常量,会直接把求值后的结果硬编码到类元数据里,但Java注解对Scala类的支持确实有限,很多Scala特有的语法都兼容不了
那该怎么解决呢?最靠谱的方案是用编译期宏注解来处理,因为宏可以在编译期介入,直接对注解参数进行求值,甚至还能把结果嵌入到被注解的类里,方便运行期使用。
给你写个完整的示例:
首先定义带宏实现的注解:
import scala.annotation.StaticAnnotation import scala.language.experimental.macros import scala.reflect.macros.blackbox class FakeAnnotation(val strings: String*) extends StaticAnnotation { // 声明宏转换方法 def macroTransform(annottees: Any*): Any = macro FakeAnnotationMacro.impl } // 宏的实现类 object FakeAnnotationMacro { def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = { import c.universe._ // 从注解前缀中解析参数,并求值拿到实际值 val evaluatedValues = c.prefix.tree match { case Apply(_, argTrees) => argTrees.map { tree => // 编译期对参数树求值,拿到真实的String值 c.eval[String](c.Expr(tree)) } } // 这里可以做任何你想做的事,比如打印(编译期会输出) println(s"注解参数的实际值: ${evaluatedValues.mkString(", ")}") // 如果要把值留在运行期用,可以给被注解的类生成一个伴生对象,把值存进去 annottees.head match { case classDef: ClassDef => // 生成伴生对象,添加一个静态字段存储注解值 val companion = q""" object ${classDef.name.toTermName} { val annotationValues = $evaluatedValues } """ // 返回类定义和伴生对象 Block(List(classDef, companion), Literal(Constant(()))) case _ => annottees.head } } }
然后使用这个注解:
val someVar = "Test" @FakeAnnotation(someVar) class TestClass
这样一来:
- 编译的时候,宏会直接求值
someVar得到"Test",你可以在宏逻辑里做代码生成、校验等操作 - 运行期你可以通过
TestClass.annotationValues直接拿到求值后的列表,这里就是List("Test")
如果你的需求只是编译期需要这个值,那直接在宏里用就行;如果要运行期用,就像上面那样把值嵌入到伴生对象里就好。
备注:内容来源于stack exchange,提问作者Tanin
相关产品推荐
相关产品推荐

