Пакет scala.reflect.api
пакет api
Члены типа
трактAnnotations extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот тракт предоставляет поддержку аннотаций для API отражения.
В Scala аннотации относятся к одной из двух категорий:
-
Аннотации Java: аннотации на определениях, сгенерированных компилятором Java, т.е. подтипы java.lang.annotation.Annotation, прикрепленные к определениям программы.
Аннотации Scala: аннотации на определениях или типах, сгенерированных компилятором Scala.
При компиляции аннотации Scala, наследующей от scala.annotation.StaticAnnotation, она сохраняется как специальные атрибуты в соответствующем файле .class, а не как аннотация Java. Обратите внимание, что простое наследование от scala.annotation.Annotation недостаточно для сохранения соответствующих метаданных для рефлексии во время выполнения.
Как аннотации Java, так и аннотации Scala представлены типизированными деревьями, содержащими вызовы конструкторов, соответствующие аннотации. Например, аннотация в @ann(1, 2) class C представлена как q"@new ann(1, 2)".
В отличие от Java-рефлексии, Scala-рефлексия не поддерживает вычисление вызовов конструкторов, хранящихся в аннотациях, в подлежащие объекты. Например, невозможно перейти от @ann(1, 2) class C к ann(1, 2), поэтому необходимо анализировать деревья, представляющие аргументы аннотации, для ручного извлечения соответствующих значений. В этих целях аргументы аннотации можно получить с помощью annotation.tree.children.tail.
Дополнительную информацию об Annotation см. в Руководстве по отражению: аннотации, имена, области и многое другое
трактConstants extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Согласно разделу 6.24 «Постоянные выражения» спецификации языка Scala, некоторые выражения (называемые *постоянными выражениями*) могут быть вычислены компилятором Scala во время компиляции.
Экземпляры scala.reflect.api.Constants#Constant представляют собой определенные виды этих выражений (со значениями, хранящимися в поле value и его строго типизированных представлениях под именами booleanValue, intValue и т.д.), а именно:
-
Литералы примитивных типов (байты, короткие, целые, длинные, плавающие, двойные, символы, булевы и пустые).
Строковые литералы.
Ссылки на классы (обычно создаваемые с помощью scala.Predef#classOf).
Ссылки на значения перечислений.
Эти константы используются для представления литералов в деревьях абстрактного синтаксиса (узел scala.reflect.api.Trees#Literal) и литеральных аргументов для аннотаций файлов .class Java (класс scala.reflect.api.Annotations#LiteralArgument).
Пример
Поле value заслуживает некоторого объяснения. Примитивные и строковые значения представлены сами по себе, а ссылки на классы и перечисления — несколько окольным путем.
Ссылки на классы представлены экземплярами scala.reflect.api.Types#Type (потому что при обработке компилятором Scala ссылки на классы, подлежащий класс во время выполнения может еще не быть скомпилирован). Для преобразования такой ссылки в класс во время выполнения следует использовать метод runtimeClass такого зеркала, как scala.reflect.api.Mirrors#RuntimeMirror (самый простой способ получить такое зеркало — использовать scala.reflect.runtime.package#currentMirror).
Ссылки на значения перечислений представлены экземплярами scala.reflect.api.Symbols#Symbol, которые на JVM указывают на методы, возвращающие подлежащие значения перечислений. Чтобы просмотреть подлежащее перечисление или получить значение ссылки на перечисление во время выполнения, необходимо использовать scala.reflect.api.Mirrors#RuntimeMirror (самый простой способ получить такое зеркало — снова scala.reflect.runtime.package#currentMirror).
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 {}
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)).asInstanceOf[LiteralArgument].value
val classRef = jarg("classRef").typeValue
println(showRaw(classRef)) // TypeRef(ThisType(<empty>), JavaAnnottee, List())
println(cm.runtimeClass(classRef)) // class JavaAnnottee
val enumRef = jarg("enumRef").symbolValue
println(enumRef) // value BAR
val siblings = enumRef.owner.info.decls
val enumValues = siblings.filter(sym => sym.isVal && sym.isPublic)
println(enumValues) // Scope{
// final val FOO: JavaSimpleEnumeration;
// final val BAR: JavaSimpleEnumeration
// }
// doesn't work because of https://github.com/scala/bug/issues/6459
// val enumValue = mirror.reflectField(enumRef.asTerm).get
val enumClass = cm.runtimeClass(enumRef.owner.asClass)
val enumValue = enumClass.getDeclaredField(enumRef.name.toString).get(null)
println(enumValue) // BAR
}
трактExprs extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Тракт, который определяет типизированные обёртки деревьев и операции над ними для использования в Scala Reflection.
Expr оборачивает дерево абстрактного синтаксиса (scala.reflect.api.Trees#Tree) и маркирует его типом (scala.reflect.api.Types#Type).
Обычно Expr создаются с помощью scala.reflect.api.Universe#reify, в этом случае компилятор генерирует scala.reflect.api.TreeCreator для данного выражения, а также создаёт дополнительный scala.reflect.api.TypeTags#WeakTypeTag, соответствующий типу этого выражения.
Expr также можно создавать вручную с помощью компаньон-объекта Expr, но тогда на программисте лежит ответственность за предоставление TreeCreator. Рефлексия во время компиляции с помощью макросов, как описано в scala.reflect.macros.Aliases, предоставляет более простой способ ручного создания экземпляров exprs. Ручное создание, однако, очень редко необходимо при работе с рефлексией во время выполнения.
Expr можно мигрировать из одного зеркала в другое, используя метод in. Миграция означает, что все символические ссылки на классы/объекты/пакеты в выражении переопределяются в новом зеркале (обычно с помощью загрузчика классов этого зеркала). Стандартная вселенная для Expr обычно scala.reflect.runtime#universe, стандартное зеркало обычно scala.reflect.runtime#currentMirror.
трактFlagSets extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Тракт, определяющий наборы флагов и операции над ними.
Flag используются для предоставления модификаторов для абстрактных синтаксических деревьев, которые представляют определения через поле flags в scala.reflect.api.Trees#Modifiers. Деревья, которые принимают модификаторы:
-
scala.reflect.api.Trees#ClassDef. Классы и тракты.
scala.reflect.api.Trees#ModuleDef. Объекты.
scala.reflect.api.Trees#ValDef. Объявления `val`, `var`, параметры и аннотации типа `self`.
scala.reflect.api.Trees#DefDef. Методы и конструкторы.
scala.reflect.api.Trees#TypeDef. Псевдонимы типов, абстрактные члены типа и параметры типа.
Например, чтобы создать класс с именем C, необходимо написать что-то вроде:
ClassDef(Modifiers(NoFlags), TypeName("C"), Nil, ...)
Здесь набор флагов пустой.
Чтобы сделать C закрытым, необходимо написать что-то вроде:
ClassDef(Modifiers(PRIVATE), TypeName("C"), Nil, ...)
Флаги также можно объединять с помощью вертикальной черты (|). Например, закрытый окончательный класс записывается примерно так:
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 можно использовать для проверки окончательности). Эти методы проверки могут потребовать приведения с asTerm, asType или asClass , так как некоторые флаги имеют смысл только для определённых видов символов.
Обратите внимание: Эта часть API отражения рассматривается как кандидат на переработку. В будущих версиях API отражения наборы флагов, скорее всего, будут заменены чем-то другим.
Для получения дополнительной информации о FlagSet и других аспектах отражения Scala, см. Руководство по отражению
тракт ImplicitTags extends AnyRef
Тэги, которые сохраняют идентичность абстрактных типов при стирании. Могут использоваться для сопоставления с образцом, проверки экземпляров, сериализации и т. п.
тракт Internals extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот тракт собирает API, иногда необходимые для выполнения операций низкого уровня с отражающими артефактами. См. Internals#InternalApi для получения дополнительной информации о характере, полезности и гарантиях совместимости этих API.
тракт JavaUniverse extends Universe
ЭКСПЕРИМЕНТАЛЬНО
Уточнение scala.reflect.api.Universe для отражения во время выполнения с помощью загрузчиков классов JVM.
Это уточнение снабжает зеркала возможностями отражения для JVM. JavaMirror может преобразовывать артефакты отражения Scala (символы и типы) в артефакты отражения Java (классы) и наоборот. Он также может выполнять рефлексивные вызовы (получение/установка значений полей, вызов методов и т. д.).
См. Руководство по отражению для получения подробной информации о том, как использовать отражение во время выполнения.
тракт Liftables extends AnyRef
абстрактный класс Mirror[U <: Universe with Singleton] extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Базовый класс для всех зеркал.
См. scala.reflect.api.Mirrors или Руководство по отражению для получения полного обзора Mirror.
- U
тип вселенной, к которой принадлежит это зеркало.
тракт Mirrors extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот трейт предоставляет поддержку для зеркал в Scala Reflection API.
Mirror являются центральной частью Scala Reflection. Вся информация, предоставляемая отражением, становится доступной через Mirror. В зависимости от типа информации, которую нужно получить, или действия отражения, необходимо использовать разные типы зеркал. Зеркала «классификатора» могут быть использованы для получения представлений о типах и членах. Из классификатора Mirror, можно получить более специализированные «вызывающие» Mirror (наиболее часто используемые зеркала), которые реализуют рефлексивные вызовы, такие как вызовы методов/конструкторов и доступ к полям.
Два типа зеркал:
-
«Зеркала классификатора». Эти зеркала переводят имена в символы (через методы
staticClass/staticModule/staticPackage).
«Вызывающие» зеркала. Эти зеркала реализуют рефлексивные вызовы (через методы MethodMirror.apply, FieldMirror.get, и т. д.). Эти «вызывающие» зеркала являются наиболее часто используемыми типами зеркал.Зеркала на этапе компиляции
Зеркала на этапе компиляции Mirror используют только классификаторы Mirror для загрузки Symbol по имени.
Точка входа в классификаторы Mirror – через scala.reflect.macros.blackbox.Context#mirror или scala.reflect.macros.whitebox.Context#mirror. Типичные методы, использующие классификаторы Mirror включают scala.reflect.api.Mirror#staticClass, scala.reflect.api.Mirror#staticModule и scala.reflect.api.Mirror#staticPackage. Например:
import scala.reflect.macros.blackbox.Context
case class Location(filename: String, line: Int, column: Int)
object Macros {
def currentLocation: Location = macro impl
def impl(c: Context): c.Expr[Location] = {
import c.universe._
val pos = c.macroApplication.pos
val clsLocation = c.mirror.staticModule("Location") // get symbol of "Location" object
c.Expr(Apply(Ident(clsLocation), List(Literal(Constant(pos.source.path)), Literal(Constant(pos.line)), Literal(Constant(pos.column)))))
}
}
Примечание: Есть несколько высокоуровневых альтернатив, которые можно использовать, чтобы избежать ручного поиска символов. Например, typeOf[Location.type].termSymbol (или typeOf[Location].typeSymbol если нам нужно ClassSymbol), которые являются безопасными для типа, так как нам не нужно использовать String для поиска Symbol.
Зеркала на этапе выполнения
Зеркала на этапе выполнения Mirror используют как классификаторы, так и вызывающие Mirror.
Точка входа в Mirror для использования во время выполнения – через ru.runtimeMirror(<classloader>), где ru – scala.reflect.runtime.universe.
Результат вызова scala.reflect.api.JavaUniverse#runtimeMirror – это зеркало классификатора типа scala.reflect.api.Mirrors#ReflectiveMirror, которое может загружать символы по имени, как описано выше (в разделе «Этап компиляции»).
Зеркало классификатора может создавать вызывающие зеркала, которые включают: scala.reflect.api.Mirrors#InstanceMirror, scala.reflect.api.Mirrors#MethodMirror, scala.reflect.api.Mirrors#FieldMirror, scala.reflect.api.Mirrors#ClassMirror и scala.reflect.api.Mirrors#ModuleMirror.
Примеры взаимодействия этих двух типов Mirror приведены ниже.
Типы зеркал, их случаи использования и примеры
scala.reflect.api.Mirrors#ReflectiveMirror. Используется для загрузки Symbol по имени и в качестве точки входа в вызывающие зеркала. Точка входа: val m = ru.runtimeMirror(<classloader>). Пример:
scala> val ru = scala.reflect.runtime.universe ru: scala.reflect.api.JavaUniverse = ... scala> val m = ru.runtimeMirror(getClass.getClassLoader) m: reflect.runtime.universe.Mirror = JavaMirror ...
scala.reflect.api.Mirrors#InstanceMirror. Используется для создания вызывающих Mirror для методов и полей, а также для вложенных классов и вложенных объектов (модулей). Точка входа: val im = m.reflect(<value>). Пример:
scala> class C { def x = 2 }
defined class C
scala> val im = m.reflect(new C)
im: reflect.runtime.universe.InstanceMirror = instance mirror for C@3442299e
scala.reflect.api.Mirrors#MethodMirror. Используется для вызова методов экземпляров (в Scala есть только методы экземпляров – методы объектов являются методами экземпляров объектов, доступных через ModuleMirror.instance). Точка входа: val mm = im.reflectMethod(<method symbol>). Пример:
scala> val methodX = typeOf[C].decl(TermName("x")).asMethod
methodX: reflect.runtime.universe.MethodSymbol = method x
scala> val mm = im.reflectMethod(methodX)
mm: reflect.runtime.universe.MethodMirror = method mirror for C.x: scala.Int (bound to C@3442299e)
scala> mm()
res0: Any = 2
scala.reflect.api.Mirrors#FieldMirror. Используется для получения/установки полей экземпляров (в Scala есть только поля экземпляров – поля объектов являются методами экземпляров объектов, доступными через ModuleMirror.instance). Точка входа: val fm = im.reflectMethod(<field or accessor symbol>). Пример:
scala> class C { val x = 2; val y = 3 }
defined class C
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror ...
scala> val im = m.reflect(new C)
im: reflect.runtime.universe.InstanceMirror = instance mirror for C@5f0c8ac1
scala> val fieldX = typeOf[C].decl(TermName("x")).asTerm.accessed.asTerm
fieldX: reflect.runtime.universe.TermSymbol = value x
scala> val fmX = im.reflectField(fieldX)
fmX: reflect.runtime.universe.FieldMirror = field mirror for C.x (bound to C@5f0c8ac1)
scala> fmX.get
res0: Any = 2
scala> fmX.set(3) // NOTE: can set an underlying value of an immutable field!
scala> val fieldY = typeOf[C].decl(TermName("y")).asTerm.accessed.asTerm
fieldY: reflect.runtime.universe.TermSymbol = variable y
scala> val fmY = im.reflectField(fieldY)
fmY: reflect.runtime.universe.FieldMirror = field mirror for C.y (bound to C@5f0c8ac1)
scala> fmY.get
res1: Any = 3
scala> fmY.set(4)
scala> fmY.get
res2: Any = 4
scala.reflect.api.Mirrors#ClassMirror. Используется для создания вызывающих зеркал для конструкторов. Точки входа: для статических классов val cm1 = m.reflectClass(<class symbol>), для вложенных классов val mm2 = im.reflectClass(<class symbol>). Пример:
scala> case class C(x: Int) defined class C scala> val m = ru.runtimeMirror(getClass.getClassLoader) m: reflect.runtime.universe.Mirror = JavaMirror ... scala> val classC = typeOf[C].typeSymbol.asClass classC: reflect.runtime.universe.Symbol = class C scala> val cm = m.reflectClass(classC) cm: reflect.runtime.universe.ClassMirror = class mirror for C (bound to null) scala> val ctorC = typeOf[C].decl(ru.nme.CONSTRUCTOR).asMethod ctorC: reflect.runtime.universe.MethodSymbol = constructor C scala> val ctorm = cm.reflectConstructor(ctorC) ctorm: reflect.runtime.universe.MethodMirror = constructor mirror for C.<init>(x: scala.Int): C (bound to null) scala> ctorm(2) res0: Any = C(2)
scala.reflect.api.Mirrors#ModuleMirror. Используется для получения одиночных экземпляров объектов. Точки входа: для статических объектов (модулей) val mm1 = m.reflectModule(<module symbol>), для вложенных объектов (модулей) val mm2 = im.reflectModule(<module symbol>). Пример:
scala> object C { def x = 2 }
defined module C
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror ...
scala> val objectC = typeOf[C.type].termSymbol.asModule
objectC: reflect.runtime.universe.ModuleSymbol = object C
scala> val mm = m.reflectModule(objectC)
mm: reflect.runtime.universe.ModuleMirror = module mirror for C (bound to null)
scala> val obj = mm.instance
obj: Any = C$@1005ec04
Для получения дополнительной информации о Mirrors см. Руководство по отражению: Зеркала
трейт Names extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот трейт определяет Name в Scala Reflection и операции с ними.
Имена — это простые оболочки для строк. Имя имеет два подтипа TermName и TypeName, которые различают имена терминов (таких как объекты или члены) и типов. Термин и тип с одинаковым именем могут сосуществовать в объекте.
Чтобы найти метод map (который является термином), объявленный в классе List, можно сделать следующее:
scala> typeOf[List[_]].member(TermName("map"))
res0: reflect.runtime.universe.Symbol = method map
Чтобы найти член типа, можно следовать той же процедуре, используя TypeName вместо этого.
Для получения дополнительной информации о создании и использовании Name см. Руководство по отражению: Аннотации, имена, области видимости и многое другое
трейт Position extends Attachments
ЭКСПЕРИМЕНТАЛЬНО
Позиция отслеживает происхождение символов и узлов дерева. Они часто используются при отображении предупреждений и ошибок, чтобы указать неверную точку в программе.
Каждая непустая позиция ссылается на SourceFile и три смещения символов в нём: начало, конец и точка. Точка — это позиция, где располагается ^ при выводе сообщения об ошибке, обычно имя.
Позиция диапазона может быть обозначена как прозрачная, что освобождает её от соблюдения последующих инвариантов. Если у прозрачной позиции есть непрозрачные дочерние элементы, они рассматриваются так, как будто они являются прямыми дочерними элементами родительского элемента прозрачной позиции.
Примечание: некоторые из этих инвариантов фактически применяются к деревьям, которые содержат позиции, но они сформулированы так, как будто сами позиции являются родительскими/дочерними элементами для краткости.
Инвариант 1: в позиции фокуса/смещения, начало == точка == конец Инвариант 2: в позиции диапазона, начало <= точка < конец Инвариант 3: у позиции смещения нет дочернего элемента с позицией диапазона Инвариант 4: каждый дочерний элемент позиции диапазона, являющийся частью родительской позиции диапазона, содержится внутри своего родителя Инвариант 5: непрозрачные дочерние элементы позиции диапазона перекрываются максимум в одной точке
Следующие тесты полезны для позиций:
pos.isDefined true, если позиция не является UndefinedPosition (эти позиции NoPosition и FakePos) pos.isRange true, если позиция является диапазоном (непрозрачным или прозрачным), что подразумевает начало < конец pos.isOpaqueRange true, если позиция является непрозрачным диапазоном
Следующие методы доступа предоставляются — будет брошено исключение, если будут попытки получить точку/начало/конец для UndefinedPosition.
pos.source Файл источника позиции или NoSourceFile, если он недоступен pos.point Смещение точки pos.start (включительно) смещение начала или точка позиции смещения pos.end (исключительно) смещение конца или точка позиции смещения
Следующие методы преобразования часто используются:
pos.focus Преобразует позицию диапазона в позицию смещения, сосредоточенную на точке pos.makeTransparent Преобразует непрозрачный диапазон в прозрачный диапазон
Для получения дополнительной информации о Position см. Руководство по рефлексии: Аннотации, имена, области видимости и многое другое
trait Positions extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот трайт определяет понятие позиций и операций над ними.
- См. также
trait Printers extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Утилиты для красивой печати scala.reflect.api.Trees и scala.reflect.api.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: 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-типизатор.
Обратите внимание, что, хотя это представление генерирует, по-видимому, корректные деревья, которые можно использовать в реализации макроса, это обычно не так. Символы не полностью представлены (отображаются только их имена). Таким образом, этот метод лучше всего подходит для простого просмотра AST, заданного корректным кодом Scala.
scala> showRaw(tree)
res1: String = Block(List(
ClassDef(Modifiers(FINAL), TypeName("C"), List(), Template(
List(Ident(TypeName("AnyRef"))),
noSelfType,
List(
DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(),
Block(List(
Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.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"))),
noSelfType,
List(
DefDef[2](Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree[3](),
Block[1](List(
Apply[4](Select[5](Super[6](This[3](TypeName("C")), tpnme.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: reflect.runtime.universe.Type
scala> show(tpe)
res0: String = scala.AnyRef{def x: Int; val y: scala.List[Int]}
Как и метод showRaw для scala.reflect.api.Trees, showRaw для scala.reflect.api.Types обеспечивает визуализацию AST Scala, над которым работает Scala-типизатор.
// showRaw has already been discussed above
scala> showRaw(tpe)
res1: String = RefinedType(
List(TypeRef(ThisType(scala), TypeName("AnyRef"), List())),
Scope(
TermName("x"),
TermName("y")))
printIds и/или printKinds могут дополнительно передаваться в качестве аргументов в вызов showRaw, который дополнительно отображает уникальные идентификаторы символов.
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))
Для получения более подробной информации о Printer и других аспектах рефлексии Scala, см. Руководство по рефлексии
trait Quasiquotes extends AnyRef
trait Scopes extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот трайт предоставляет поддержку областей видимости в API рефлексии.
Объект области видимости, как правило, сопоставляет имена символам, доступным в соответствующей лексической области видимости. Области видимости могут быть вложенными. Однако базовый тип, экспонируемый API рефлексии, предоставляет только минимальный интерфейс, представляющий область видимости как итерируемый набор символов.
В редких случаях, когда необходимо создать область видимости вручную, например, для заполнения членов scala.reflect.api.Types#RefinedType, существует функция newScopeWith.
Дополнительные функции доступны в областях видимости членов, которые возвращаются members и decls, определенные в scala.reflect.api.Types#TypeApi. Такие области видимости поддерживают метод sorted, который сортирует члены в порядке объявления.
trait StandardDefinitions extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Все стандартные символы и типы Scala.
К этим стандартным определениям можно получить доступ, используя definitions. Обычно они импортируются с помощью импорта со звёздочкой, import definitions._, и перечислены в scala.reflect.api.StandardDefinitions#DefinitionsApi.
trait StandardLiftables extends AnyRef
trait StandardNames extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Стандартные имена — это имена, необходимые для создания деревьев или для отражения объектов Scala. Например, CONSTRUCTOR (также <init> в JVM) необходим для создания и вызова конструкторов.
К этим стандартным именам можно обратиться, используя nme для имён терминов и tpnme для имён типов
- См. также
Names API для имён в Scala-рефлексии.
trait Symbols extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот трейт определяет символы и операции над ними.
Символы используются для установления связей между именем и сущностью, на которую оно ссылается, например, классом или методом. Любая определяемая вами сущность, которой можно присвоить имя в Scala, имеет ассоциированный символ.
Символы содержат всю доступную информацию о объявлении сущности (класс/объект/трейт и т.д.) или члена (значения/переменные/определения и т.д.), и таким образом являются интегральной абстракцией, центральной как для рефлексии во время выполнения, так и для макросов.
Символ может предоставить массу информации, начиная от базового метода name доступного для всех символов, до более сложных концепций, таких как получение baseClasses от ClassSymbol. Другие распространенные случаи использования символов включают проверку сигнатур членов, получение параметров типа класса, получение типа параметра метода или определение типа поля.
Пример использования рефлексии во время выполнения; получение сигнатуры типа метода:
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> class C[T] { def test[U](x: T)(y: U): Int = ??? }
defined class C
scala> val test = typeOf[C[Int]].member(TermName("test")).asMethod
test: reflect.runtime.universe.MethodSymbol = method test
scala> test.info
res0: reflect.runtime.universe.Type = [U](x: T)(y: U)scala.Int
Символы организованы в иерархии. Например, символ, представляющий параметр метода, принадлежит соответствующему символу метода, символ метода принадлежит своему содержащему классу, класс принадлежит содержащему пакету и так далее.
Некоторые типы узлов дерева, такие как Ident (ссылки на идентификаторы) и Select (ссылки на члены) предоставляют метод symbol для получения символа, представляющего их объявление. Во время фазы проверки типов компилятор ищет символ по имени и области видимости и устанавливает поле symbol узлов дерева.
Для получения дополнительной информации об использовании Symbol и связанных тонкостях, см. Руководство по рефлексии: Символы
абстрактный класс TreeCreator extends Serializable
Создатель деревьев, учитывающий зеркала.
Этот класс используется внутренне Scala Reflection и не рекомендуется для использования в клиентском коде.
трейт Trees extends AnyRef
ЭКСПЕРИМЕНТАЛЬНО
Этот трейт определяет типы узлов, используемые в деревьях абстрактного синтаксиса (AST) Scala, и операции над ними.
Деревья являются основой абстрактного синтаксиса Scala, используемого для представления программ. Они также называются деревьями абстрактного синтаксиса и обычно сокращаются до AST.
В Scala Reflection API, которые производят или используют Tree:
-
Аннотации, которые используют деревья для представления своих аргументов, показаны в Annotation.scalaArgs.
reify, специальный метод в scala.reflect.api.Universe, который принимает выражение и возвращает AST, представляющий это выражение.
Макросы и компиляция во время выполнения с помощью наборов инструментов, которые оба используют деревья в качестве среды представления программы.
Деревья неизменяемы, за исключением трех полей pos, symbol и tpe, которые присваиваются при проверке типа дерева для атрибутирования его информацией, собранной проверяющим типы.
Примеры
Следующее создает AST, представляющий литерал 5 на языке Scala:
Literal(Constant(5))
Следующее создаёт AST, представляющий print("Hello World").
Apply(Select(Select(This(TypeName("scala")), TermName("Predef")), TermName("print")), List(Literal(Constant("Hello World"))))
Следующее создает AST из литерала 5, а затем использует showRaw для вывода его в читаемом формате.
import scala.reflect.runtime.universe.{ reify, showRaw }
print( showRaw( reify{5}.tree ) )` // prints Literal(Constant(5))
Для получения дополнительной информации о Tree см. Руководство по рефлексии: Символы, Деревья, Типы.
абстрактный класс TypeCreator extends Serializable
Создатель типов, учитывающий зеркала.
Этот класс используется внутренне Scala Reflection и не рекомендуется для использования в клиентском коде.
трейт TypeTags extends AnyRef
A TypeTag[T] encapsulates the runtime type representation of some type T. Like scala.reflect.Manifest, the prime use case of TypeTags is to give access to erased types. However, TypeTags should be considered to be a richer replacement of the pre-2.10 notion of a Manifest, that are, in addition, fully integrated with Scala reflection.
There exist three different types of TypeTags:
-
scala.reflect.api.TypeTags#TypeTag.
A full type descriptor of a Scala type. For example, a
TypeTag[List[String]] contains all type information, in this case, of type scala.List[String].
scala.reflect.ClassTag. A partial type descriptor of a Scala type. For example, a
ClassTag[List[String]] contains only the erased class type information, in this case, of type scala.collection.immutable.List. ClassTags provide access only to the runtime class of a type. Analogous to scala.reflect.ClassManifest
scala.reflect.api.TypeTags#WeakTypeTag. A type descriptor for abstract types (see description below).
Like Manifests, TypeTags are always generated by the compiler, and can be obtained in three ways:
#1 Via the methods typeTag, classTag, or weakTypeTag
For example:
import scala.reflect.runtime.universe._ val tt = typeTag[Int] import scala.reflect._ val ct = classTag[String]
Each of these methods constructs a TypeTag[T] or ClassTag[T] for the given type argument T.
#2 Using an implicit parameter of type TypeTag[T], ClassTag[T], or WeakTypeTag[T]
For example:
import scala.reflect.runtime.universe._
def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)
#3 Context bound of a type parameter
...on methods or classes. The above example can be implemented as follows:
import scala.reflect.runtime.universe._
def paramInfo[T: TypeTag](x: T): Unit = {
val targs = typeOf[T] match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
scala> paramInfo(42)
type of 42 has type arguments List()
scala> paramInfo(List(1, 2))
type of List(1, 2) has type arguments List(Int)
WeakTypeTags
WeakTypeTag[T] generalizes TypeTag[T]. Unlike a regular TypeTag, components of its type representation can be references to type parameters or abstract types. However, WeakTypeTag[T] tries to be as concrete as possible, i.e. if type tags are available for the referenced type arguments or abstract types, they are used to embed the concrete types into the WeakTypeTag[T].
Continuing the example above:
def weakParamInfo[T](x: T)(implicit tag: WeakTypeTag[T]): Unit = {
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
println(s"type of $x has type arguments $targs")
}
scala> def foo[T] = weakParamInfo(List[T]())
foo: [T]=> Unit
scala> foo[Int]
type of List() has type arguments List(T)
TypeTags and Manifests
TypeTags correspond loosely to the pre-2.10 notion of scala.reflect.Manifests. While scala.reflect.ClassTag corresponds to scala.reflect.ClassManifest and scala.reflect.api.TypeTags#TypeTag mostly corresponds to scala.reflect.Manifest, other pre-2.10 Manifest types do not have a direct correspondence with a 2.10 "Tag" type.
-
scala.reflect.OptManifest is not supported.
This is because
Tags can reify arbitrary types, so they are always available. -
There is no equivalent for scala.reflect.AnyValManifest. Instead, one can compare their
Tag with one of the base Tags (defined in the corresponding companion objects) in order to find out whether or not it represents a primitive value class. Additionally, it's possible to simply use <tag>.tpe.typeSymbol.isPrimitiveValueClass.
There are no replacement for factory methods defined in the Manifest companion objects. Instead, one could generate corresponding types using the reflection APIs provided by Java (for classes) and Scala (for types). Certain manifest operations(i.e., <:<, >:> and typeArguments) are not supported.
Instead, one could use the reflection APIs provided by Java (for classes) and Scala (for types).
In Scala 2.10, scala.reflect.ClassManifests are deprecated, and it is planned to deprecate scala.reflect.Manifest in favor of TypeTags and ClassTags in an upcoming point release. Thus, it is advisable to migrate any Manifest-based APIs to use Tags.
For more information about TypeTags, see the Reflection Guide: TypeTags
- See also
scala.reflect.ClassTag, scala.reflect.api.TypeTags#TypeTag, scala.reflect.api.TypeTags#WeakTypeTag
trait Types extends AnyRef
EXPERIMENTAL
A trait that defines types and operations on them.
Type instances represent information about the type of a corresponding symbol. This includes its members (methods, fields, type parameters, nested classes, traits, etc.) either declared directly or inherited, its base types, its erasure and so on. Types also provide operations to test for type conformance or equivalence or for widening.
To instantiate a type, most of the time, the scala.reflect.api.TypeTags#typeOf method can be used. It takes a type argument and produces a Type instance which represents that argument. For example:
scala> typeOf[List[Int]] res0: reflect.runtime.universe.Type = scala.List[Int]
In this example, a scala.reflect.api.Types#TypeRef is returned, which corresponds to the type constructor List applied to the type argument Int.
In the case of a generic type, you can also combine it with other types using scala.reflect.api.Types#appliedType. For example:
scala> val intType = typeOf[Int] intType: reflect.runtime.universe.Type = Int scala> val listType = typeOf[List[_]] listType: reflect.runtime.universe.Type = List[_] scala> appliedType(listType.typeConstructor, intType) res0: reflect.runtime.universe.Type = List[Int]
Note: Method typeOf does not work for types with type parameters, such as typeOf[List[A]] where A is a type parameter. In this case, use scala.reflect.api.TypeTags#weakTypeOf instead.
For other ways to instantiate types, see the corresponding section of the Reflection Guide.
Common Operations on Types
Types are typically used for type conformance tests or are queried for declarations of members or inner types.
-
Subtyping Relationships can be tested using
<:< and weak_<:<.
Type Equality can be checked with =:=. It's important to note that == should not be used to compare types for equality-- == can't check for type equality in the presence of type aliases, while =:= can. Types can be queried for members and declarations by using the members and declarations methods (along with their singular counterparts member and declaration), which provide the list of definitions associated with that type. For example, to look up the map method of List, one can do:
scala> typeOf[List[_]].member(TermName("map"))
res1: reflect.runtime.universe.Symbol = method map
For more information about memberss, see the Reflection Guide: Symbols, Trees, and Types
абстрактный класс Universe extends Symbols with Types with FlagSets with Scopes with Names with Trees with Constants with Annotations with Positions with Exprs with TypeTags with ImplicitTags with StandardDefinitions with StandardNames with StandardLiftables with Mirrors with Printers with Liftables with Quasiquotes with Internals
ЭКСПЕРИМЕНТАЛЬНО
Universe предоставляет полный набор операций рефлексии, которые позволяют рефлексивно исследовать отношения типов Scala, такие как членство или подтипизация.
scala.reflect.api.Universe имеет два специализированных под-вселенных для разных сценариев. scala.reflect.api.JavaUniverse добавляет операции, которые связывают символы и типы с базовыми классами и значениями времени выполнения экземпляра JVM — это можно рассматривать как Universe , которое следует использовать во всех типичных случаях использования рефлексии Scala. scala.reflect.macros.Universe добавляет операции, которые позволяют макросам получать доступ к выбранным структурам данных и операциям компилятора — этот тип Universe должен существовать только в реализации макроса Scala.
Universe можно рассматривать как точку входа в рефлексию Scala. Он включает в себя и, следовательно, предоставляет интерфейс к следующим основным типам:
-
Типы представляют типы
Символы представляют определения
Деревья представляют абстрактные синтаксические деревья
Имена представляют имена терминов и типов
Аннотации представляют аннотации
Позиции представляют исходные позиции узлов дерева
Наборы флагов представляют наборы флагов, которые применяются к символам и деревьям определений
Константы представляют константы времени компиляции.
Чтобы получить Universe для использования с рефлексией Scala во время выполнения, просто убедитесь, что используется или импортируется scala.reflect.runtime.universe._
scala> import scala.reflect.runtime.universe._ import scala.reflect.runtime.universe._ scala> typeOf[List[Int]] res0: reflect.runtime.universe.Type = scala.List[Int] scala> typeOf[Either[String, Int]] res1: reflect.runtime.universe.Type = scala.Either[String,Int]
Чтобы получить Universe для использования в макросе Scala, используйте scala.reflect.macros.blackbox.Context#universe или scala.reflect.macros.whitebox.Context#universe. Например:
def printf(format: String, params: Any*): Unit = macro impl
def impl(c: Context)(format: c.Expr[String], params: c.Expr[Any]*): c.Expr[Unit] = {
import c.universe._
...
}
Дополнительную информацию о Universe см. в руководстве по рефлексии: Руководство по рефлексии: Вселенные
© 2002-2019 EPFL, with contributions from Lightbend.
Licensed under the Apache License, Version 2.0.
https://www.scala-lang.org/api/2.13.0/scala-reflect/scala/reflect/api/index.html
ЭКСПЕРИМЕНТАЛЬНО
API отражения Scala (расположен в scala-reflect.jar).
В Scala 2.10.0 API отражения Scala и его реализация имеют статус «экспериментальный». Это означает, что API и документация неполны и могут быть изменены несовместимым образом в 2.10.1 (как в бинарном, так и в исходном коде). Это также означает, что в реализации есть известные проблемы.
Следующие типы являются основой API отражения Scala и служат хорошей отправной точкой для получения информации об отражении Scala:
Для получения дополнительной информации об отражении Scala см. Руководство по отражению