Get TypeTag[A] from Class[A]

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

Leave a Comment