В отражении Scala, почему функция отражения в TypeTag все еще имеет стирание типа?

Учитывая следующую программу scala:

val arr: Seq[String] = Seq("abc", "def")
val cls = arr.head.getClass
println(cls)
val ttg: TypeTag[Seq[String]] = typeOf[Seq[String]]
val fns = ttg.tpe
  .members
val fn = fns
  .filter(_.name.toString == "head")
  .head                           // Unsafely access it for now, use Option and map under normal conditions
  .asMethod                       // Coerce to a method symbol

val fnTp = fn.returnType
println(fnTp)

val fnCls = ttg.mirror.runtimeClass(fnTp)
assert(fnTp == cls)

Поскольку TypeTag имеет информацию как о Seq, так и о String, я ожидал, что fn.returnType даст правильный результат «String», но в этом случае я получил следующий вывод программы:

cls = class java.lang.String
fnTp = A

И впоследствии выбросить это исключение:

A needed class was not found. This could be due to an error in your runpath. Missing class: no Java class corresponding to A found
java.lang.NoClassDefFoundError: no Java class corresponding to A found
    at scala.reflect.runtime.JavaMirrors$JavaMirror.typeToJavaClass(JavaMirrors.scala:1258)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.runtimeClass(JavaMirrors.scala:202)
    at scala.reflect.runtime.JavaMirrors$JavaMirror.runtimeClass(JavaMirrors.scala:65)

Очевидно, что тип String был стерт, оставив только подстановочный знак типа «A».

Почему TypeTag не может дать правильный стертый тип, как предполагалось?


person tribbloid    schedule 08.06.2016    source источник


Ответы (1)


Seq.head определяется как def head: A. А fn — это всего лишь символ метода head из универсального класса Seq[A], он ничего не знает о конкретном типе. Таким образом, его returnType точно такое же, как A, как определено в Seq.

Если вы хотите знать, каким будет этот A в каком-то конкретном Type, вам придется указать это явно. Например, вы можете использовать infoIn в символе метода:

scala> val fnTp = fn.infoIn(ttg.tpe)
fnTp: reflect.runtime.universe.Type = => String

scala> val fnRetTp = fnTp.resultType
fnRetTp: reflect.runtime.universe.Type = String
person Kolmar    schedule 09.06.2016
comment
Извините, ваше решение выдало ошибку компиляции: значение infoIn не является членом scala.reflect.runtime.universe.MethodSymbol val fnTp = fn.infoIn(ttg.tpe) - person tribbloid; 09.06.2016
comment
@tribbloid Какую версию Scala вы используете? Вот ссылка на метод в документации: scala-lang.org/api/2.11.8/scala-reflect/ - person Kolmar; 09.06.2016
comment
2.10.5 (проблема с зависимостями не позволяет мне обновиться до 2.11.7). Я обнаружил, что аналогом infoIn является typeSignatureIn, но еще не нашел аналог для resultType. - person tribbloid; 09.06.2016
comment
Интересно, что я также не могу найти resultType как метод Reflect.runtime.universe.Type в scala 2.11.x. Если тип call-by-name в Scala — это особый тип/класс, то как читать из него метаданные? - person tribbloid; 09.06.2016
comment
@tribbloid В версии 2.11 resultType находится в признаке TypeApi, который расширен классом Type: scala-lang.org/api/2.11.8/scala-reflect/ - person Kolmar; 09.06.2016
comment
Также я думаю, что вы можете использовать fnTp.typeSymbol, чтобы получить символ String. Например: ttg.mirror.runtimeClass(fn.typeSignatureIn(ttg.tpe).typeSymbol.asClass) - person Kolmar; 09.06.2016
comment
Ты прав. Кроме того, .asClass дал вам только стертый символ класса, если вы хотите получить его тип в scala 2.10.x, вы должны сначала преобразовать fnTp в NullaryMethodType, прежде чем применять .resultType - person tribbloid; 10.06.2016