Scala 中使用 TypeTag 和 ClassTag 的多态实例化 [英] Polymorphic instantiation in Scala using TypeTag and ClassTag
问题描述
在 Scala 2.9 中可以实现多态实例化
In Scala 2.9 one could implement polymorphic instantiation as
def newInstance[T](implicit m: Manifest[T]) =
m.erasure.newInstance.asInstanceOf[T]
但从 2.10 开始,Manifest
被替换为 TypeTag
,我不清楚如何使用 TypeTag
实现类似的功能.我希望 TypeTag 版本保留所有可用的类型信息.
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
.
I would prefer if the TypeTag version preserved all available type information.
我知道以上仅适用于不需要构造函数参数的特征/类,然后它并不总是有效,但它足以满足我的需要.如果我能把新的反射 API 做得更好,那就太好了.
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. If I can do better the new reflection APIs that would be great.
推荐答案
TypeTag
还不是 Manifest
的替代品,因为它是实验性和不稳定的 Scala 反射的一部分.你现在绝对不应该将它用于生产.
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.
对于您展示的用例,其中只需要运行时类(不是带有泛型等的完整类型信息),Scala 2.10 引入了 ClassTag
,您可以这样使用:
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]
或:
def newInstance[T](implicit ct: ClassTag[T]) =
ct.runtimeClass.newInstance.asInstanceOf[T]
无论如何,Manifest
还没有被弃用,所以我想你仍然可以使用它.
Anyway, Manifest
isn't deprecated yet, so I guess you can still use it.
使用 TypeTag
实现相同的效果:
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]
}
上面的解决方案仍然使用了一些Java反射.如果我们想要纯粹并且只使用 Scala 反射,这就是解决方案:
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]
}
这篇关于Scala 中使用 TypeTag 和 ClassTag 的多态实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!