In Scala 2.11+ reflection, how to reliably convert a TypeTag and a Manifest into each other?

喜你入骨 提交于 2020-01-30 11:01:11

问题


In this post:

Is it possible to convert a TypeTag to a Manifest?

It is indicated that a TypeTag can be converted into a Manifest using the following code:

  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]]
  }

It doesn't work, as demonstrated in the following test case:

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)
  }
}

Output:

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

Evidently this indicates that type erasure has been plaguing the conversion, and TypeTag, despite being designed to avoid type erasure, can only resolve to the dependent type example.T without getting the correct type arguments from underlying type Map[String, Int].

So what is the way to convert TypeTag and Manifest into each other that doesn't suck?


回答1:


If you replace

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

with

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

this improves toManifest but not completely:

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

vs.

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

i.e. Java primitive int vs. scala.Int.

The problem is in the second .runtimeClass in

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

mirror.runtimeClass(t) is int, ClassTag[T](mirror.runtimeClass(t)) is Int but ClassTag[T](mirror.runtimeClass(t)).runtimeClass is again int.

So try to improve toManifest more

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


来源:https://stackoverflow.com/questions/59637768/in-scala-2-11-reflection-how-to-reliably-convert-a-typetag-and-a-manifest-into

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!