Как отразить конкретные возвращаемые типы для методов классов, определенных во время выполнения, с помощью Scala ToolBox?

Отражая метод foo() класса Cls, мы можем легко получить конкретный тип возвращаемого значения, используя следующее.

class Cls {
  def foo() =
    List("A", "B")
}

val classType = ru.typeOf[Cls]
val classMirror = toolbox.mirror.reflectClass(classType.typeSymbol.asClass)
val ctorSymbol = classType.decl(ru.termNames.CONSTRUCTOR).asMethod
val methodSymb = classType.decl(ru.TermName("foo")).asMethod
val ctor = classMirror.reflectConstructor(ctorSymbol)
val instance = ctor()
val im = toolbox.mirror.reflect(instance)
val foo = im.reflectMethod(methodSymb)
println(foo())  // List(A, B)
println(methodSymb.returnType) // List[String]

Однако, если мы определим класс Cls во время выполнения через набор инструментов, а затем воспользуемся тем же процессом отражения метода, мы получим только абстрактный тип возвращаемого значения (List) метода foo().

import ru._

val tree =
  q"""class Cls {
        def foo() =
          List("A", "B")
    }""".asInstanceOf[ru.ClassDef]

val classSymbol = toolbox.define(tree).asClass
val classType = classSymbol.selfType

val classMirror = toolbox.mirror.reflectClass(classType.typeSymbol.asClass)
val ctorSymbol = classType.decl(ru.termNames.CONSTRUCTOR).asMethod
val methodSymb = classType.decl(ru.TermName("foo")).asMethod
val ctor = classMirror.reflectConstructor(ctorSymbol)
val instance = ctor()
val im = toolbox.mirror.reflect(instance)
val foo = im.reflectMethod(methodSymb)
println(foo())  // List("A", "B")
println(methodSymb.returnType)  // List

Почему эти фрагменты кода дают разные результаты?

Как мы можем отразить конкретный тип возврата метода foo(), если Cls определен во время выполнения с использованием набора инструментов?


person Erp12    schedule 12.11.2020    source источник


Ответы (1)


Думаю, дело в стирании шрифта.

Попробуйте заменить

val classType = classSymbol.selfType

с участием

val classType = toolbox.eval(q"scala.reflect.runtime.universe.typeOf[$classSymbol]").asInstanceOf[ru.Type]

Тогда methodSymb.returnType это List[String].

person Dmytro Mitin    schedule 12.11.2020
comment
Спасибо за исправление! Не могли бы вы поделиться некоторыми дополнительными подробностями (или указателями на документы), объясняющими, почему стирание типа будет различным в этих двух вариантах? Принято в любом случае. - person Erp12; 13.11.2020
comment
@ Erp12 Ну, я не уверен на 100% (нужно было бы глубже отладить генерацию кода панели инструментов), но я предполагаю следующее. Давайте подумаем о рабочем процессе кода. Есть время компиляции и время выполнения основного кода. С набором инструментов внутри среды выполнения основного кода появляется время компиляции и время выполнения набора инструментов. Во время компиляции панели инструментов тип возврата List[String], во время выполнения панели инструментов это просто List[_] из-за стирания. - person Dmytro Mitin; 13.11.2020
comment
@ Erp12 Я подозреваю, что когда мы делаем classSymbol.selfType, мы работаем во время выполнения панели инструментов, а с toolbox.eval(q"scala.reflect... мы захватываем тип из времени компиляции панели инструментов (tb.eval(tree) равно tb.compile(tree).apply()). Toolbox не позволяет лучше контролировать генерацию кода. Я предполагаю, что с помощью ручного создания кода (см. код из question) вы лучше контролируете, как вы компилируете и запускаете свой код. - person Dmytro Mitin; 13.11.2020
comment
@ Erp12 В этом вопросе у меня уже была ситуация, когда мне приходилось фиксировать некоторую информацию о типе из панели инструментов с помощью eval (см. Фрагмент кода в моем ответе на этот вопрос с помощью tb.eval(q"...TypeInformation.of(classOf...). - person Dmytro Mitin; 13.11.2020