从 2.11 版开始,scala 反射功能(尤其是 wrt 注释)的(当前)状态是什么? [英] What is the (current) state of scala reflection capabilities, especially wrt annotations, as of version 2.11?

查看:31
本文介绍了从 2.11 版开始,scala 反射功能(尤其是 wrt 注释)的(当前)状态是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

scala 似乎是 JVM 领域的一个很好的补充.它让我想起了嵌套在 JVM 世界中的 C++、C# 和 Swift 的奇怪混合体.

scala appears to be a wonderful addition to the JVM universe. It reminds me of a strange hybrid of C++, C#, and Swift, nested in the JVM world.

但是,由于缺少文档或文档过时,scala 的许多功能可能无法使用.

However, many of scala's features may be inaccessible due to lacking or out-of-date documentation.

就其反射能力而言,这似乎尤其正确.

This seems especially true with respect to its reflection capabilities.

例如,我正在评估是否可以使用 Scala 注释在运行时或编译时扩充 Scala 类.我正在使用最新的 Scala 版本 2.11.作为一个激励示例,假设我创建了一个 case class SimpleAnnotation() extends StaticAnnotation.我想在运行时找到所有带有该注释的 case classes.

For instance, I am assessing whether or not it would be possible to augment scala classes at either runtime or compiletime using scala annotations. I am using the latest scala version 2.11. As a motivating example, let's say I make a case class SimpleAnnotation() extends StaticAnnotation. I would like to, at runtime, find all case classes with that annotation.

这可能是最典型和最普通的注释用例.

This is probably the most typical and vanilla use case for annotations.

在 C# 和 Java 中,在运行时确定给定的类是否被注解相对简单.这是一种规范的用例,具有规范的答案.然而,在 Scala 中,我不清楚我应该做什么来实现这种行为,甚至是否可能.特别是,在浏览了之前关于 scala 注释和反射的一些资料后,我感到疑惑:

In C# and in Java it is relatively straightforward to determine at runtime whether a given class is annotated. This is a canonical sort of use case with a canonical sort of answer. Yet in scala it is unclear to me what I ought to do to achieve this behavior, or even whether it is possible. In particular, after scanning some previous material on scala annotations and reflection, I am left wondering:

  • 这可能吗?
  • 这只能在运行时或编译时实现吗?
  • 这是否只能在 Scala 2.10 版之前或之后?
  • 这是否只能在 Scala 类上使用 Java 注释?
  • 为什么getClass[AnnotatedClass].getAnnotations会返回这样看似乱码的信息?
  • 为什么在 Scala 中,宏和反射似乎混为一谈?
  • Is this possible?
  • Is this only possible at runtime or complile time?
  • Is this only possible before or after scala version 2.10?
  • Is this only possible using Java annotations on scala classes?
  • Why does getClass[AnnotatedClass].getAnnotations return such seemingly garbled information?
  • Why are macros and reflection seemingly conflated in scala?

感谢您提供任何指导......我相信我不是唯一一个感到困惑的人.

Any guidance is appreciated... and I'm sure I am not the only one who is confused.

推荐答案

反射和宏共享很多 API,因为它们基本上是同一个东西:元编程.您可以生成和执行代码,您必须反映类型等等.当然也有一些区别:在编译时你不能反映运行时实例,在运行时你不能访问方法的内部结构、作用域和在编译期间删除的其他信息.

Reflection and Macros share a lot of the API because they are basically the same thing: Meta Programming. You can generate and execute code, you have to reflect types and so on. There are some differences of course: at compile-time you cannot reflect runtime instances and at runtime you do not get access to internal structure of methods, scope and other information that is deleted during compilation.

这两个 API 仍处于试验阶段,将来可能会在某些部分发生变化,但它们非常有用且文档齐全.Scala 是一种多功能语言,它们只是比 Java 中的 API 复杂得多.

Both APIs are still experimental and will probably change in the future in some parts, but they are very usable and also quite well documented. Scala being such a versatile language they are just much more complex than the APIs in Java.

这个文档带给你很远:

http://www.scala-lang.org/api/2.11.7/scala-reflect/

http://www.scala-lang.org/api/2.11.7/scala-compiler/

http://docs.scala-lang.org/overviews/(底部页面)

这个 getClass[AnnotatedClass].getAnnotations 只给你 Java 注释,要获得 Scala 注释,你必须获得 Scala 类型,而不仅仅是类.

This getClass[AnnotatedClass].getAnnotations gives you only Java Annotations, to get Scala Annotations you have to get the Scala Type instead of only the class.

可以在运行时和编译时访问反射,但是有三种注解:

It is possible to access reflections during runtime as well as during compile time, however there are three kinds of annotations:

  1. 仅在代码中的普通注释:可以从编译单元中的宏访问这些注释,在该编译单元中调用宏,宏可以访问 AST

  1. Plain annotations that are only in the code: These can be accessed from macros in the compilation unit where the macro is called where the macro gets access to the AST

在编译单元上共享的静态注解:这些可以通过 Scala 反射 api 访问

StaticAnnotations that are shared over compilation units: these can be accessed via scala reflection api

ClassfileAnnotations:这些表示存储为 java 注释的注释.如果您想通过 Java 反射 API 访问它们,您必须在 Java 中定义它们.

ClassfileAnnotations: these represent annotations stored as java annotations. If you want to access them via the Java Reflection API you have to define them in Java though.

这是一个例子:

@SerialVersionUID(1) class Blub

现在,我们可以通过这种方式获得注解:

Now, we can get the annotation this way:

import scala.reflect.runtime.universe._
val a = typeOf[Blub].typeSymbol.annotations.head

我们实际得到的不是注解的实例.运行时环境只是给我们写在字节码中的内容:生成注释的 scala 代码.你可以打印出你得到的 AST:

What we actually get is not an instance of an annotation. The runtime environment just gives us what is written in the byte code: the scala code generating the annotation. You can print out the AST that you get:

showRaw(a.tree)

现在,这已经是一个相当复杂的结构了,但是我们可以使用模式匹配来分解它:

Now, this is already a quite complicated structure, but we can decompose it using pattern matching:

val Apply(_, List(AssignOrNamedArg(_,Literal(Constant(value))))) = a.tree
val uid = value.asInstanceOf[Long]

这对于非常简单的注释是可以的(但我们可以用 Java 编写它们并依赖 JVM 为我们创建实例).如果我们真的想要评估该代码并生成注释类的实例怎么办?(对于@SerialVersionUID,这对我们没有多大帮助,因为该类实际上并没有提供对 id 的访问权限...)我们也可以这样做:

This is OK for very simple annotations (but we could write those in Java and rely on the JVM creating instances for us). What if we actually want to evaluate that code and generate an instance of the annotation class? (For @SerialVersionUID this would not help us much as the class actually does not give as access to the id...) We can also do that:

case class MyAnnotation(name: String) extends annotation.ClassfileAnnotation

@MyAnnotation(name = "asd")
class MyClass

object MyApp extends App {
  import reflect.runtime.universe._
  import scala.reflect.runtime.currentMirror
  import scala.tools.reflect.ToolBox
  val toolbox = currentMirror.mkToolBox()
  val annotation = typeOf[MyClass].typeSymbol.annotations.head
  val instance =  toolbox.eval(toolbox.untypecheck(annotation.tree))
        .asInstanceOf[MyAnnotation]
  println(instance.name)
}

请注意,这将调用编译器,这需要一些时间,尤其是在您第一次这样做时.复杂的元编程真的应该在 Scala 编译时完成.Java 中的很多东西只能在运行时完成,因为您只能进行运行时元编程(嗯,有注解处理器,但它们更有限).

Note, that this will call the compiler which takes a little time, especially if you do it for the first time. Sophisticated meta programming should really be done at compile time in Scala. A lot of stuff in Java is only done at runtime because you can only have runtime meta programming (Well, there are annotation processors, but they are more limited).

这篇关于从 2.11 版开始,scala 反射功能(尤其是 wrt 注释)的(当前)状态是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆