Dan*_*ler 5 reflection scala scala-2.10
在Scala 2.9中,可以实现多态实例化
def newInstance[T](implicit m: Manifest[T]) =
m.erasure.newInstance.asInstanceOf[T]
Run Code Online (Sandbox Code Playgroud)
但是从2.10开始Manifest被替换为TypeTag,并且我不清楚如何实现类似的东西TypeTag.如果TypeTag版本保留了所有可用的类型信息,我更愿意.
我知道上面只适用于不需要构造函数args的traits/classes,然后它并不总是有效,但它对我需要的东西运行良好.如果我能做得更好,新的反射API会很棒.
TypeTagManifest因为它是实验性和不稳定的Scala反射的一部分,所以还不能替代它.你现在肯定不应该把它用于生产.
对于你展示的用例,只需要运行时类(不是带有泛型等的完整类型信息),引入了Scala 2.10 ClassTag,你可以这样使用:
def newInstance[T: ClassTag] =
implicitly[ClassTag[T]].runtimeClass.newInstance.asInstanceOf[T]
Run Code Online (Sandbox Code Playgroud)
要么:
def newInstance[T](implicit ct: ClassTag[T]) =
ct.runtimeClass.newInstance.asInstanceOf[T]
Run Code Online (Sandbox Code Playgroud)
无论如何,Manifest还没有弃用,所以我猜你仍然可以使用它.
编辑:
使用TypeTag以达到相同的:
import scala.reflect.runtime.universe._
def newInstance[T: TypeTag] = {
val clazz = typeTag[T].mirror.runtimeClass(typeOf[T])
clazz.newInstance.asInstanceOf[T]
}
Run Code Online (Sandbox Code Playgroud)
上面的解决方案仍然使用一些Java反射.如果我们想要纯粹并且只使用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]
}
Run Code Online (Sandbox Code Playgroud)
如果你想支持传递参数,这是我在 2.11 中做的一个技巧:
def newInstance[T : ClassTag](init_args: AnyRef*): T = {
classTag[T].runtimeClass.getConstructors.head.newInstance(init_args: _*).asInstanceOf[T]
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
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)
Run Code Online (Sandbox Code Playgroud)