Get TypeTag[A] from Class[A]

后端 未结 2 1508
夕颜
夕颜 2020-12-01 22:04

I have createOld method that I need to override and I cannot change it. I would like to use TypeTag to pattern match provided type in createN

2条回答
  •  既然无缘
    2020-12-01 23:02

    It is possible to create a TypeTag from a Class using Scala reflection, though I'm not sure if this implementation of TypeCreator is absolutely correct:

    import scala.reflect.runtime.universe._
    
    def createOld[A](c: Class[A]): A = createNew {
      val mirror = runtimeMirror(c.getClassLoader)  // obtain runtime mirror
      val sym = mirror.staticClass(c.getName)  // obtain class symbol for `c`
      val tpe = sym.selfType  // obtain type object for `c`
      // create a type tag which contains above type object
      TypeTag(mirror, new TypeCreator {
        def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
          if (m eq mirror) tpe.asInstanceOf[U # Type]
          else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
      })    
    }
    

    However, you don't really need full TypeTag if you don't need to inspect generic parameters and full Scala type information. You can use ClassTags for that:

    def createNew[A: ClassTag]: A = {
      val result = classTag[A].runtimeClass match {
        case a if a.isAssignableFrom(classOf[C1]) => new C1()
        case a if a.isAssignableFrom(classOf[C2]) => new C2()
      }
      result.asInstanceOf[A]
    }
    

    Or with some implicit sugar:

    implicit class ClassTagOps[T](val classTag: ClassTag[T]) extends AnyVal {
      def <<:(other: ClassTag[_]) = classTag.runtimeClass.isAssignableFrom(other.runtimeClass)
    }
    
    def createNew[A: ClassTag]: A = {
      val result = classTag[A] match {
        case a if a <<: classTag[C1] => new C1()
        case a if a <<: classTag[C2] => new C2()
      }
      result.asInstanceOf[A]
    }
    

    You can simplify that even further by using plain old Java newInstance() method:

    def createNew[A: ClassTag]: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]
    

    This, of course, would only work if you don't need different constructor parameters for different classes.

    Calling this createNew from createOld is much simpler than the one with TypeTags:

    def createOld[A](c: Class[A]): A = createNew(ClassTag[A](c))
    

提交回复
热议问题