【问题标题】:In Scala 2.11+ reflection, how to reliably convert a TypeTag and a Manifest into each other?在 Scala 2.11+ 反射中,如何可靠地将 TypeTag 和 Manifest 相互转换?
【发布时间】:2020-01-10 08:15:29
【问题描述】:

在这篇文章中:

Is it possible to convert a TypeTag to a Manifest?

表明可以使用以下代码将TypeTag转换为Manifest:

  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 互相转换成不烂的呢?

【问题讨论】:

    标签: scala type-erasure scala-reflect


    【解决方案1】:

    如果你替换

    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 原语 intscala.Int

    问题出在第二个.runtimeClass

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

    mirror.runtimeClass(t)intClassTag[T](mirror.runtimeClass(t))IntClassTag[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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多