В рефлексии Scala 2.11+, как надежно конвертировать TypeTag и Manifest друг в друга? - PullRequest
0 голосов
/ 08 января 2020

В этом посте:

Можно ли конвертировать TypeTag в манифест?

Указывается, что TypeTag можно конвертировать в манифест с помощью следующий код:

  def toManifest[T:TypeTag]: Manifest[T] = {
    val t = typeTag[T]
    val mirror = t.mirror
    def toManifestRec(t: Type): Manifest[_] = {
      val clazz = ClassTag[T](mirror.runtimeClass(t)).runtimeClass
      if (t.typeArgs.length == 1) {
        val arg = toManifestRec(t.typeArgs.head)
        ManifestFactory.classType(clazz, arg)
      } else if (t.typeArgs.length > 1) {
        val args = t.typeArgs.map(x => toManifestRec(x))
        ManifestFactory.classType(clazz, args.head, args.tail: _*)
      } else {
        ManifestFactory.classType(clazz)
      }
    }
    toManifestRec(t.tpe).asInstanceOf[Manifest[T]]
  }

Не работает , как показано в следующем тестовом примере:

object TypeTag2Manifest {

  class Example {

    type T = Map[String, Int]
  }
  val example = new Example

}

class TypeTag2Manifest extends FunSpec {

  import org.apache.spark.sql.catalyst.ScalaReflection.universe._
  import TypeTag2Manifest._

  it("can convert") {

    val t1 = implicitly[TypeTag[example.T]]
    val v1 = toManifest(t1)
    val v2 = implicitly[Manifest[example.T]]

    assert(v1 == v2)
  }
}

Выход:

scala.collection.immutable.Map did not equal scala.collection.immutable.Map[java.lang.String, Int]
ScalaTestFailureLocation: com.tribbloids.spike.scala_spike.reflection.TypeTag2Manifest at (TypeTag2Manifest.scala:52)
Expected :scala.collection.immutable.Map[java.lang.String, Int]
Actual   :scala.collection.immutable.Map

Очевидно, это указывает на то, что стирание типа мешало преобразованию, и TypeTag, несмотря на то, что был разработан, чтобы избежать стирания типа, может преобразовываться только в зависимый тип example.T без получения правильных аргументов типа из базового типа Map[String, Int].

Так как же конвертировать TypeTag и Manifest друг в друга, которые не сосут?

1 Ответ

2 голосов
/ 10 января 2020

Если вы замените

toManifestRec(t.tpe).asInstanceOf[Manifest[T]]

на

toManifestRec(t.tpe.dealias).asInstanceOf[Manifest[T]]

, это улучшит toManifest, но не полностью:

scala.collection.immutable.Map[java.lang.String, int]

против.

scala.collection.immutable.Map[java.lang.String, Int]

т.е. Java примитив int против scala.Int.

Проблема во втором .runtimeClass в

val clazz = ClassTag[T](mirror.runtimeClass(t)).runtimeClass 

mirror.runtimeClass(t) это int, ClassTag[T](mirror.runtimeClass(t)) это Int, но ClassTag[T](mirror.runtimeClass(t)).runtimeClass снова int.

Так что попробуйте улучшить toManifest еще

def toManifest[T: TypeTag]: Manifest[T] = {
  val t = typeTag[T]
  val mirror = t.mirror
  def toManifestRec(t: Type): Manifest[_] = {
    ClassTag[T](mirror.runtimeClass(t)) match {
      case ClassTag.Byte    => Manifest.Byte
      case ClassTag.Short   => Manifest.Short
      case ClassTag.Char    => Manifest.Char
      case ClassTag.Int     => Manifest.Int
      case ClassTag.Long    => Manifest.Long
      case ClassTag.Float   => Manifest.Float
      case ClassTag.Double  => Manifest.Double
      case ClassTag.Boolean => Manifest.Boolean
      case ClassTag.Unit    => Manifest.Unit
      case ClassTag.Object  => Manifest.Object
      case ClassTag.Nothing => Manifest.Nothing
      case ClassTag.Null    => Manifest.Null
      case classTag         =>
        val clazz = classTag.runtimeClass
        if (t.typeArgs.length >= 1) {
          val args = t.typeArgs.map(x => toManifestRec(x))
          ManifestFactory.classType(clazz, args.head, args.tail: _*)
        } else {
          ManifestFactory.classType(clazz)
        }
    }
  }
  toManifestRec(t.tpe.dealias).asInstanceOf[Manifest[T]]
}

val t1 = implicitly[TypeTag[example.T]] //TypeTag[TypeTag2Manifest.example.T]
val v1 = toManifest(t1) //scala.collection.immutable.Map[java.lang.String, Int]
val v2 = implicitly[Manifest[example.T]] //scala.collection.immutable.Map[java.lang.String, Int]
v1 == v2 //true
...