scala> val finals = listTpe.decls.sorted.filter(_.isFinal)
finals: List(method isEmpty, method map, method collect, method flatMap, method takeWhile, method span, method foreach, method reverse, method foldRight, method length, method lengthCompare, method forall, method exists, method contains, method find, method mapConserve, method toList)
除了抽象语法树的基本类型 scala.reflect.api.Trees#Tree
外,类型化树还可以表示为类型 scala.reflect.api.Exprs#Expr
的实例。 Expr
封装了一个抽象语法树和一个内部类型标记,以提供对树类型的访问。 Expr
主要用于简单方便地创建类型化抽象语法树,以便在宏中使用。在大多数情况下,这涉及方法 reify
和 splice
(有关详细信息,请参阅 宏指南)。
标志和标志集
标志用于通过 scala.reflect.api.Trees#Modifiers
的 flags
字段为表示定义的抽象语法树提供修饰符。接受修饰符的树是
scala.reflect.api.Trees#ClassDef
。类和特质。
scala.reflect.api.Trees#ModuleDef
。对象。
scala.reflect.api.Trees#ValDef
。Vals、vars、参数和自身类型注释。
scala.reflect.api.Trees#DefDef
。方法和构造函数。
scala.reflect.api.Trees#TypeDef
。类型别名、抽象类型成员和类型参数。
例如,要创建一个名为 C
的类,可以编写类似以下内容
ClassDef(Modifiers(NoFlags), TypeName("C"), Nil, ...)
此处,标志集为空。要使 C
为私有,可以编写类似以下内容
ClassDef(Modifiers(PRIVATE), TypeName("C"), Nil, ...)
标志还可以与竖线运算符 (|
) 结合使用。例如,私有 final 类可以编写类似以下内容
ClassDef(Modifiers(PRIVATE | FINAL), TypeName("C"), Nil, ...)
所有可用标志的列表在 scala.reflect.api.FlagSets#FlagValues
中定义,可通过 scala.reflect.api.FlagSets#Flag
获得。(通常,为此使用通配符导入,例如, import scala.reflect.runtime.universe.Flag._
。)
定义树被编译为符号,以便这些树的修饰符上的标志转换为结果符号上的标志。与树不同,符号不公开标志,而是提供遵循 isXXX
模式的测试方法(例如, isFinal
可用于测试 final 性)。在某些情况下,这些测试方法需要使用 asTerm
、asType
或 asClass
进行转换,因为某些标志仅对特定类型的符号有意义。
注意:反射 API 的这一部分被认为是重新设计的候选部分。在反射 API 的未来版本中,标志集很可能被其他内容替换。
Scala 规范称为常量表达式的某些表达式可以在编译时由 Scala 编译器求值。以下类型的表达式是编译时常量(请参阅 Scala 语言规范的第 6.24 节)
基本值类的字面量(Byte、Short、Int、Long、Float、Double、Char、Boolean 和 Unit) - 直接表示为对应的类型。
字符串字面量 - 表示为字符串的实例。
对类的引用,通常使用 scala.Predef#classOf 构造 - 表示为 类型。
对 Java 枚举值引用 - 表示为 符号。
常量表达式用于表示
抽象语法树中的文字(参见 scala.reflect.api.Trees#Literal
),以及
Java 类文件注释的文字参数(参见 scala.reflect.api.Annotations#LiteralArgument
)。
scala> Literal(Constant(5))
val res6: reflect.runtime.universe.Literal = 5
以上表达式创建一个 AST,表示 Scala 源代码中的整数文字 5
。
Constant
是“虚拟案例类”的一个示例,即,一个可以构建其实例并对其进行匹配的类,就好像它是一个案例类一样。两种类型 Literal
和 LiteralArgument
都具有一个 value
方法,该方法返回文字底层的编译时常量。
Constant(true) match {
case Constant(s: String) => println("A string: " + s)
case Constant(b: Boolean) => println("A Boolean value: " + b)
case Constant(x) => println("Something else: " + x)
assert(Constant(true).value == true)
类引用表示为 scala.reflect.api.Types#Type
的实例。可以使用 RuntimeMirror
(例如 scala.reflect.runtime.currentMirror
)的 runtimeClass
方法将此类引用转换为运行时类。(必须从类型转换为运行时类,因为当 Scala 编译器处理类引用时,底层运行时类可能尚未编译。)
Java 枚举值引用表示为符号(scala.reflect.api.Symbols#Symbol
的实例),在 JVM 上指向返回底层枚举值的方法。RuntimeMirror
可用于检查底层枚举或获取对枚举的引用的运行时值。
// Java source:
enum JavaSimpleEnumeration { FOO, BAR }
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface JavaSimpleAnnotation {
Class<?> classRef();
JavaSimpleEnumeration enumRef();
@JavaSimpleAnnotation(
classRef = JavaAnnottee.class,
enumRef = JavaSimpleEnumeration.BAR
public class JavaAnnottee {}
// Scala source:
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{currentMirror => cm}
object Test extends App {
val jann = typeOf[JavaAnnottee].typeSymbol.annotations(0).javaArgs
def jarg(name: String) = jann(TermName(name)) match {
// Constant is always wrapped in a Literal or LiteralArgument tree node
case LiteralArgument(ct: Constant) => value
case _ => sys.error("Not a constant")
val classRef = jarg("classRef").value.asInstanceOf[Type]
println(showRaw(classRef)) // TypeRef(ThisType(), JavaAnnottee, List())
println(cm.runtimeClass(classRef)) // class JavaAnnottee
val enumRef = jarg("enumRef").value.asInstanceOf[Symbol]
println(enumRef) // value BAR
val siblings = enumRef.owner.typeSignature.decls
val enumValues = siblings.filter(sym => sym.isVal && sym.isPublic)
println(enumValues) // Scope {
// final val FOO: JavaSimpleEnumeration;
// final val BAR: JavaSimpleEnumeration
val enumClass = cm.runtimeClass(enumRef.owner.asClass)
val enumValue = enumClass.getDeclaredField(enumRef.name.toString).get(null)
println(enumValue) // BAR
用于漂亮地打印 Trees
和 Types
的实用程序。
方法 show
显示反射工件的“美化”表示形式。此表示形式为用户提供 Scala 代码的去糖化 Java 表示形式。例如
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> def tree = reify { final class C { def x = 2 } }.tree
tree: scala.reflect.runtime.universe.Tree
scala> show(tree)
res0: String =
final class C extends AnyRef {
def <init>() = {
super.<init>();
def x = 2
方法 showRaw
显示给定反射对象的内部结构,作为 Scala 抽象语法树 (AST),这是 Scala 类型检查器操作的表示形式。
请注意,虽然此表示形式似乎生成了正确的树,人们可能会认为可以在宏实现中使用这些树,但这通常并非如此。符号没有得到完全表示(只有它们的名称)。因此,此方法最适合用于简单地检查给定一些有效 Scala 代码的 AST。
scala> showRaw(tree)
res1: String = Block(List(
ClassDef(Modifiers(FINAL), TypeName("C"), List(), Template(
List(Ident(TypeName("AnyRef"))),
emptyValDef,
List(
DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(),
Block(List(
Apply(Select(Super(This(typeNames.EMPTY), typeNames.EMPTY), termNames.CONSTRUCTOR), List())),
Literal(Constant(())))),
DefDef(Modifiers(), TermName("x"), List(), List(), TypeTree(),
Literal(Constant(2))))))),
Literal(Constant(())))
方法 showRaw
还可以打印正在检查的工件旁边的 scala.reflect.api.Types
。
scala> import scala.tools.reflect.ToolBox // requires scala-compiler.jar
import scala.tools.reflect.ToolBox
scala> import scala.reflect.runtime.{currentMirror => cm}
import scala.reflect.runtime.{currentMirror=>cm}
scala> showRaw(cm.mkToolBox().typeCheck(tree), printTypes = true)
res2: String = Block[1](List(
ClassDef[2](Modifiers(FINAL), TypeName("C"), List(), Template[3](
List(Ident[4](TypeName("AnyRef"))),
emptyValDef,
List(
DefDef[2](Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree[3](),
Block[1](List(
Apply[4](Select[5](Super[6](This[3](TypeName("C")), typeNames.EMPTY), ...))),
Literal[1](Constant(())))),
DefDef[2](Modifiers(), TermName("x"), List(), List(), TypeTree[7](),
Literal[8](Constant(2))))))),
Literal[1](Constant(())))
[1] TypeRef(ThisType(scala), scala.Unit, List())
[2] NoType
[3] TypeRef(NoPrefix, TypeName("C"), List())
[4] TypeRef(ThisType(java.lang), java.lang.Object, List())
[5] MethodType(List(), TypeRef(ThisType(java.lang), java.lang.Object, List()))
[6] SuperType(ThisType(TypeName("C")), TypeRef(... java.lang.Object ...))
[7] TypeRef(ThisType(scala), scala.Int, List())
[8] ConstantType(Constant(2))
方法 show
可用于生成类型的可读字符串表示形式
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> def tpe = typeOf[{ def x: Int; val y: List[Int] }]
tpe: scala.reflect.runtime.universe.Type
scala> show(tpe)
res0: String = scala.AnyRef{def x: Int; val y: scala.List[Int]}
与 scala.reflect.api.Trees
的 showRaw
方法类似,scala.reflect.api.Types
的 showRaw
提供了 Scala 类型检查器操作的 Scala AST 的可视化。
scala> showRaw(tpe)
res1: String = RefinedType(
List(TypeRef(ThisType(scala), TypeName("AnyRef"), List())),
Scope(
TermName("x"),
TermName("y")))
showRaw
方法还具有命名参数 printIds
和 printKinds
,两者均具有默认参数 false
。当将 true
传递给它们时,showRaw
还会显示符号的唯一标识符及其类型(包、类型、方法、getter 等)。
scala> showRaw(tpe, printIds = true, printKinds = true)
res2: String = RefinedType(
List(TypeRef(ThisType(scala#2043#PK), TypeName("AnyRef")#691#TPE, List())),
Scope(
TermName("x")#2540#METH,
TermName("y")#2541#GET))
位置(Position 特征的实例)用于跟踪符号和树节点的来源。它们通常在显示警告和错误时使用,以指示程序中的不正确点。位置指示源文件中的列和行(从源文件开头算起的偏移量称为其“点”,有时使用起来不太方便)。它们还包含所引用的行的内容。并非所有树或符号都有位置;使用 NoPosition
对象指示缺少的位置。
位置可以仅引用源文件中的单个字符,也可以引用范围。在后一种情况下,将使用范围位置(不是范围位置的位置也称为偏移位置)。范围位置还具有 start
和 end
偏移量。可以使用 focusStart
和 focusEnd
方法“聚焦”start
和 end
偏移量,这些方法返回位置(当在不是范围位置的位置上调用时,它们只返回 this
)。
可以使用诸如 precedes
之类的方法比较位置,如果两个位置都已定义(即,位置不是 NoPosition
),并且 this
位置的终点不比给定位置的起点大,则该方法成立。此外,可以测试范围位置是否包含(使用 includes
方法)和是否重叠(使用 overlaps
方法)。
范围位置要么是透明的,要么是不透明的(不透明)。范围位置是否不透明会影响其允许的使用,因为包含范围位置的树必须满足以下不变性
具有偏移位置的树永远不包含具有范围位置的子级
如果具有范围位置的树的子级也具有范围位置,那么子级的范围包含在父级的范围内。
同一节点的子级的非透明范围位置不重叠(这意味着它们的重叠最多为一个点)。
使用 makeTransparent
方法,可以将不透明范围位置转换为透明范围位置;所有其他位置保持不变。