Scala Annotation (注解)
来源:互联网 发布:如何去除马赛克软件 编辑:程序博客网 时间:2024/06/05 09:02
Annotation (注解)
Scala
中的注解语法与Java
中类似。
标准库定义的注解相关内容在包scala.annotation
中。
注解的基本语法为:
@注解名称(注解参数...)
与Java
注解的用法类似,注解参数不是必须的,一个元素允许拥有多个注解。
自定义注解
Scala 2.10
之前,Scala
并未提供自定义注解功能,自定义注解需要在Java
源码中进行。 Scala 2.10
开始,作为Reflect
功能的一部分,Scala
提供了自定义注解支持。
与反射相关功能类似,到目前版本(Scala 2.12
)为止,注解相关功能依然是Expermental
(实验性)的,注解相关API
一直处于变化中。
Scala
中的自定义注解不是接口/特质,而是类。
自定义注解需要从注解特质中继承,Scala
中提供了两类注解特质:
scala.annotation.ClassfileAnnotation
由Java
编译器生成注解scala.annotation.StaticAnnotation
由Scala
编译器生成注解
两类注解特质都继承自基类scala.annotation.Annotation
。
定义注解类语法与普通类相同:
// 标记注解class 注解名称 extends StaticAnnotation/ClassfileAnnotation// 有参注解class 注解名称(参数表...) extends StaticAnnotation/ClassfileAnnotation
两类注解特质的基类相同,因此自定义注解类时允许同时混入两类注解特质。
继承自ClassfileAnnotation
基类的注解在使用时参数应以具名参数(named arguments)
形式传入。
继承自StaticAnnotation
基类的注解无此限制。
如下所示:
import scala.annotation.{ClassfileAnnotation, StaticAnnotation}class CustomStaticAnnotation(name: String) extends StaticAnnotationclass CustomClassfileAnnotation(name: String) extends ClassfileAnnotation@CustomStaticAnnotation("2333") //正确@CustomClassfileAnnotation("2333") //错误,Java注解需要以具名参数形式进行传入@CustomClassfileAnnotation(name = "2333") //正确class Test
解析注解
通过反射机制获取注解信息,相关API
位于scala.reflect.runtime.universe
包路径下。
获取注解:
获取类的注解:
- 使用
typeOf()
方法,获取Type
类型的类信息。 - 通过
Type.typeSymbol
获取Symbol
。 - 通过
Symbol.annotations
获取List[Annotation]
(注解列表)。
- 使用
获取方法/成员字段的注解:
- 使用
typeOf()
方法,获取Type
类型的类信息。 - 通过
Type.decls/decl()
方法筛选出目标成员的Symbol
。 - 通过
Symbol.annotations
获取List[Annotation]
(注解列表)。
- 使用
获取方法参数的注解:
- 使用
typeOf()
方法,获取Type
类型的类信息。 - 通过
Type.decls/decl()
方法筛选出目标方法的MethodSymbol
。 - 通过
MethodSymbol.paramLists
方法获取目标方法的参数表(List[List[Symbol]]
类型,方法柯里化可能会有多个参数表)。 - 通过
Symbol.annotations
获取List[Annotation]
(注解列表)。
- 使用
Scala
注解类型为scala.reflect.runtime.universe.Annotation
。
在Scala 2.11
之前,Annotation
类型提供了scalaArgs/javaArgs
等无参方法用于获取注解信息,但在Scala 2.11
版本中,这些方法已被标记为deprecated
。
应使用Annotation.tree
方法获取注解语法树,类型为scala.reflect.runtime.universe.Tree
。
如下所示:
import scala.annotation.StaticAnnotationimport scala.reflect.runtime.universe._class CustomAnnotation(name: String, num: Int) extends StaticAnnotation@CustomAnnotation("Annotation for Class", 2333)class Test { @CustomAnnotation("Annotation for Class", 6666) val ff = ""}object Main extends App { { // 获取类型注解 val tpe: Type = typeOf[Test] val symbol: Symbol = tpe.typeSymbol //获取类型符号信息 val annotation: Annotation = symbol.annotations.head val tree: Tree = annotation.tree //获取语法树 // 解析注解语法树... } { // 获取成员字段注解 val tpe: Type = typeOf[Test] val symbol: Symbol = tpe.decl(TermName("ff ")) //获取字段符号信息 val annotation: Annotation = symbol.annotations.head val tree: Tree = annotation.tree // 解析注解语法树... }}
通过scala.reflect.api.Printer.showRaw()
方法可以获取语法树的文本。
注解语法树中包含了注解参数信息,可以通过模式匹配提取。
如下所示:
import scala.annotation.StaticAnnotationimport scala.reflect.runtime.universe._class CustomAnnotation(name: String, num: Int) extends StaticAnnotation@CustomAnnotation("Annotation for Class", 2333)class Testobject Main extends App { // 获取类型注解 val tpe: Type = typeOf[Test] val symbol: Symbol = tpe.typeSymbol //获取类型符号信息 val annotation: Annotation = symbol.annotations.head val tree: Tree = annotation.tree //获取语法树 println(showRaw(tree)) //打印语法树 val Apply(_, Literal(Constant(name: String)) :: Literal(Constant(num: Int)) :: Nil) = tree println(s"Annotation args: name -> $name, num -> $num")}
输出结果:(Scala 2.12.2 && macOS 10.12.5
)
Apply(Select(New(TypeTree()), termNames.CONSTRUCTOR), List(Literal(Constant("Annotation for Class")), Literal(Constant(2333))))Annotation args: name -> Annotation for Class, num -> 2333
注意事项:
- 解析注解参数需要基于语法树结构,不要使用参数默认值特性,使用默认参数的注解生成的语法树不包含注解信息的默认值。
- 类内字段会有多个
TermSymbol
,对应不同的TermName
,包含注解信息的TermName
为字段名称 + 空格
。 - 样例类的构造器参数作为类的成员存在,但若在参数上添加注解,注解信息并未附加在字段信息中。
提取样例类构造器成员的注解信息需要以获取方法参数注解的方式进行,查找构造器方法(TermName("<init>")
),获取参数成员(Method.paramLists
)。 - 使用
Annotation.tree
方法获取注解语法树(Tree
类型),再使用Tree.tpe
方法获取注解语法树类型信息(Type
类型),与直接使用typeOf[注解类型]
获取的注解类型信息相同,可以用于比较筛选注解类型。
完整的注解解析实例,如下所示:
import scala.annotation.StaticAnnotationimport scala.reflect.runtime.universe._class CustomAnnotation(name: String, num: Int) extends StaticAnnotation { override def toString = s"Annotation args: name -> $name, num -> $num"}@CustomAnnotation("Annotation for Class", 2333)class Test { @CustomAnnotation("Annotation for Member", 6666) val ff = "" def mm(ss: String, @CustomAnnotation("Annotation for Arg", 9999) arg: Int) = ""}object Main extends App { // 获取指定类型的注解信息,通过 Annotation.tree.tpe 获取注解的 Type 类型,以此进行筛选 def getClassAnnotation[T: TypeTag, U: TypeTag] = symbolOf[T].annotations.find(_.tree.tpe =:= typeOf[U]) // 通过字段名称获取指定类型的注解信息,注意查找字段名称时添加空格 def getMemberAnnotation[T: TypeTag, U: TypeTag](memberName: String) = typeOf[T].decl(TermName(s"$memberName ")).annotations.find(_.tree.tpe =:= typeOf[U]) // 通过方法名称和参数名称获取指定类型的注解信息 def getArgAnnotation[T: TypeTag, U: TypeTag](methodName: String, argName: String) = typeOf[T].decl(TermName(methodName)).asMethod.paramLists.collect { case symbols => symbols.find(_.name == TermName(argName)) }.headOption.fold(Option[Annotation](null))(_.get.annotations.find(_.tree.tpe =:= typeOf[U])) // 解析语法树,获取注解数据 def getCustomAnnotationData(tree: Tree) = { val Apply(_, Literal(Constant(name: String)) :: Literal(Constant(num: Int)) :: Nil) = tree new CustomAnnotation(name, num) } getClassAnnotation[Test, CustomAnnotation].map(_.tree) foreach { classAnnotationTree => val classAnnotation = getCustomAnnotationData(classAnnotationTree) println(classAnnotation) } getMemberAnnotation[Test, CustomAnnotation]("ff").map(_.tree) foreach { memberAnnotationTree => val memberAnnotation = getCustomAnnotationData(memberAnnotationTree) println(memberAnnotation) } getArgAnnotation[Test, CustomAnnotation]("mm", "arg").map(_.tree) foreach { argAnnotationTree => val argAnnotation = getCustomAnnotationData(argAnnotationTree) println(argAnnotation) }}
输出结果:(Scala 2.12.2 && macOS 10.12.5
)
Annotation args: name -> Annotation for Class, num -> 2333Annotation args: name -> Annotation for Member, num -> 6666Annotation args: name -> Annotation for Arg, num -> 9999
- Scala Annotation (注解)
- 注解Annotation
- Annotation(注解)
- Annotation注解
- annotation注解
- 注解(Annotation)
- Annotation 注解
- 注解Annotation
- 注解Annotation
- Annotation(注解)
- 注解Annotation
- 注解(Annotation)
- 注解 annotation
- Annotation注解
- 注解(Annotation)
- Annotation(注解)
- 注解 annotation
- 注解 Annotation
- c# webbrowers控件调用百度地图后,鼠标读取百度地图的坐标
- 单点登录原理与简单实现
- Maven学习笔记 -- day03 Maven整合SSH
- 优化SQL查询:如何写出高性能SQL语句
- 网络爬虫系列笔记(2)——Requests库
- Scala Annotation (注解)
- Java发邮件基础篇
- R语言的科学编程与仿真-第二章答案
- 一起学ASP.NET Core 2.0学习笔记(二)- ef core2.0 及mysql provider 、Fluent API相关配置及迁移
- 1008. 数组元素循环右移问题 (20)用时18min得分20
- 中小型企业网的实现
- 解决Android Studio的Instant Run导致的错误
- UVA11624 Fire!【BFS+细心】
- 常见的web服务器软件分类