5

在 Scala 2.9 中,可以将多态实例化实现为

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

但是从 2.10开始,它Manifest被替换为TypeTag,我不清楚如何实现类似的东西TypeTag。我希望 TypeTag 版本保留所有可用的类型信息。

我知道上面只适用于不需要构造函数参数的特征/类,即使这样它并不总是有效,但它足以满足我的需要。如果我能做得更好,新的反射 API 会很棒。

4

2 回答 2

9

TypeTag还不能替代,Manifest因为它是实验性且不稳定的 Scala 反射的一部分。到目前为止,您绝对不应该将其用于生产。

对于您展示的用例,只需要运行时类(不需要具有泛型等的完整类型信息),Scala 2.10 引入了ClassTag,您可以像这样使用它:

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

或者:

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

无论如何,Manifest它还没有被弃用,所以我想你仍然可以使用它。

编辑:

使用TypeTag来达到同样的效果:

import scala.reflect.runtime.universe._

def newInstance[T: TypeTag] = {
  val clazz = typeTag[T].mirror.runtimeClass(typeOf[T])
  clazz.newInstance.asInstanceOf[T]
}

上面的解决方案仍然使用了一些 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]
}
于 2013-08-28T22:44:20.083 回答
5

如果你想支持传递参数,这是我用 2.11 做的一个技巧:

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

示例用法:

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)
于 2016-10-26T21:03:04.653 回答