简体   繁体   English

使用TypeTag和ClassTag在Scala中进行多态实例化

[英]Polymorphic instantiation in Scala using TypeTag and ClassTag

In Scala 2.9 one could implement polymorphic instantiation as 在Scala 2.9中,可以实现多态实例化

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

but as of 2.10 Manifest is being replaced with TypeTag , and it is not clear to me how to achieve something similar with TypeTag . 但是从2.10开始, Manifest正在被TypeTag取代,我不清楚如何用TypeTag实现类似的TypeTag I would prefer if the TypeTag version preserved all available type information. 如果TypeTag版本保留了所有可用的类型信息,我更愿意。

I know that the above only works for traits/classes that do not require constructor args, and ven then it does not always work, but it works well enough for what I need. 我知道上面只适用于不需要构造函数args的traits / classes,然后它并不总是有效,但它对我需要的东西运行良好。 If I can do better the new reflection APIs that would be great. 如果我能做得更好,新的反射API会很棒。

TypeTag is not yet a replacement for Manifest because it's a part of experimental and unstable Scala reflection. TypeTag还不是Manifest的替代品,因为它是实验性和不稳定Scala反射的一部分。 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: 对于你展示的用例,只需要运行时类(不是带有泛型等的完整类型信息),Scala 2.10引入了ClassTag ,你可以这样使用:

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. 无论如何, Manifest还没有被弃用,所以我猜你仍然可以使用它。

EDIT: 编辑:

Using TypeTag to achieve the same: 使用TypeTag实现相同:

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. 上面的解决方案仍然使用一些Java反射。 If we want to be puristic and use only Scala reflection, this is the solution: 如果我们想要纯粹并且只使用Scala反射,那么这就是解决方案:

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

If you want to support passing args, here's a trick I do with 2.11: 如果你想支持传递args,这是我用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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM