Каково (текущее) состояние возможностей отражения scala, особенно в отношении аннотаций, начиная с версии 2.11?

2

scala представляется прекрасным дополнением к юниверсу JVM. Это напоминает мне странный гибрид С++, С# и Swift, вложенный в мир JVM.

Однако многие функции scala могут быть недоступны из-за отсутствия или устаревшей документации.

Это особенно верно в отношении возможностей отражения.

Например, я оцениваю, можно ли расширить классы scala во время выполнения или компиляции с помощью аннотаций scala. Я использую последнюю версию scala версии 2.11. В качестве мотивирующего примера предположим, что я делаю case class SimpleAnnotation() extends StaticAnnotation. Во время выполнения я хотел бы найти все case class es с этой аннотацией.

Это, вероятно, самый типичный и ванильный вариант использования аннотаций.

В С# и в Java относительно просто определить во время выполнения, является ли данный класс аннотированным. Это канонический вид прецедента с каноническим ответом. Тем не менее, в scala мне непонятно, что я должен сделать для достижения такого поведения или даже того, возможно ли это. В частности, после сканирования какого-либо предыдущего материала по аннотации и отражению scala, мне остается задаться вопросом:

  • Возможно ли это?
  • Возможно ли это только во время выполнения или времени выполнения?
  • Возможно ли это только до или после scala версии 2.10?
  • Возможно ли это только с помощью аннотаций Java в классах scala?
  • Почему getClass[AnnotatedClass].getAnnotations возвращает такую, казалось бы, искаженную информацию?
  • Почему макросы и отражение, казалось бы, сконфигурированы в scala?

Любое руководство ценится... и я уверен, что я не единственный, кто смущен.

  • 1
    Если у вас есть 6 вопросов, вы должны задать 6 вопросов, а не один. Тем более что последний вопрос совершенно не связан с остальными.
Теги:
reflection
annotations

1 ответ

5
Лучший ответ

Reflection и Macros используют много API, потому что они в основном одно и то же: Meta Programming. Вы можете создавать и выполнять код, вы должны отражать типы и т.д. Конечно, есть некоторые отличия: во время компиляции вы не можете отображать экземпляры среды выполнения и во время выполнения вы не получаете доступ к внутренней структуре методов, области и другой информации, которая удаляется во время компиляции.

Оба API все еще являются экспериментальными и, вероятно, будут изменяться в будущем в некоторых частях, но они очень полезны, а также достаточно хорошо документированы. Scala, являясь таким универсальным языком, они намного сложнее, чем API в 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 вместо класса.

Доступ к отражениям возможен во время выполнения, а также во время компиляции, однако есть три вида аннотаций:

  • Простые аннотации, которые находятся только в коде: к ним можно получить доступ из макросов в блоке компиляции, где макрос вызывается там, где макрос получает доступ к AST

  • StaticAnnotations, которые разделяются по модулям компиляции: к ним можно получить доступ через Scala отражение api

  • ClassfileNnotations: они представляют аннотации, хранящиеся в виде аннотаций java. Если вы хотите получить к ним доступ через API Java Reflection, вы должны определить их на Java, хотя.

Вот пример:

@SerialVersionUID(1) class Blub

Теперь мы можем получить аннотацию следующим образом:

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

То, что мы фактически получаем, не является экземпляром аннотации. Среда выполнения просто дает нам то, что написано в байтовом коде: код Scala, генерирующий аннотацию. Вы можете распечатать AST, который вы получаете:

showRaw(a.tree)

Теперь это уже довольно сложная структура, но мы можем разложить ее с помощью сопоставления с образцом:

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

Это нормально для очень простых аннотаций (но мы могли бы написать их в Java и полагаться на экземпляры JVM для нас). Что делать, если мы действительно хотим оценить этот код и создать экземпляр класса аннотаций? (Для @SerialVersionUID это не поможет нам, поскольку класс фактически не дает доступа к id...) Мы также можем это сделать:

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 выполняются только во время выполнения, потому что у вас есть только мета-программирование времени выполнения (ну, есть процессоры с аннотациями, но они более ограничены).

  • 0
    Спасибо за подробный ответ на явно неоднозначный вопрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню