[英]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.