Polymorphic instantiation in Scala using TypeTag and ClassTag

前端 未结 2 431
无人及你
无人及你 2021-02-06 15:16

In Scala 2.9 one could implement polymorphic instantiation as

def newInstance[T](implicit m: Manifest[T]) =
    m.erasure.newInstance.asInstanceOf[T]


        
相关标签:
2条回答
  • 2021-02-06 15:41

    If you want to support passing args, here's a trick I do with 2.11:

    def newInstance[T : ClassTag](init_args: AnyRef*): T = {
    classTag[T].runtimeClass.getConstructors.head.newInstance(init_args: _*).asInstanceOf[T]
    }
    

    Example usage:

    scala> case class A(x:Double, y:Int)
    defined class A
    scala> newInstance[A](4.5.asInstanceOf[Object],3.asInstanceOf[Object])
    res1: A = A(4.5,3)
    
    0 讨论(0)
  • 2021-02-06 15:45

    TypeTag is not yet a replacement for Manifest because it's a part of experimental and unstable Scala reflection. You definitely shouldn't use it for production as of now.

    For the use case you showed, where only runtime class is needed (not full type information with generics etc.), Scala 2.10 introduced ClassTag, which you can use like this:

    def newInstance[T: ClassTag] =
      implicitly[ClassTag[T]].runtimeClass.newInstance.asInstanceOf[T]
    

    or:

    def newInstance[T](implicit ct: ClassTag[T]) =
      ct.runtimeClass.newInstance.asInstanceOf[T]
    

    Anyway, Manifest isn't deprecated yet, so I guess you can still use it.

    EDIT:

    Using TypeTag to achieve the same:

    import scala.reflect.runtime.universe._
    
    def newInstance[T: TypeTag] = {
      val clazz = typeTag[T].mirror.runtimeClass(typeOf[T])
      clazz.newInstance.asInstanceOf[T]
    }
    

    The above solution still uses some Java reflection. If we want to be puristic and use only Scala reflection, this is the solution:

    def newInstance[T: TypeTag]: T = {
      val tpe = typeOf[T]
    
      def fail = throw new IllegalArgumentException(s"Cannot instantiate $tpe")
    
      val noArgConstructor = tpe.member(nme.CONSTRUCTOR) match {
        case symbol: TermSymbol =>
          symbol.alternatives.collectFirst {
            case constr: MethodSymbol if constr.paramss == Nil || constr.paramss == List(Nil) => constr
          } getOrElse fail
    
        case NoSymbol => fail
      }
      val classMirror = typeTag[T].mirror.reflectClass(tpe.typeSymbol.asClass)
      classMirror.reflectConstructor(noArgConstructor).apply().asInstanceOf[T]
    }
    
    0 讨论(0)
提交回复
热议问题