簡體   English   中英

使用TypeTag和ClassTag在Scala中進行多態實例化

[英]Polymorphic instantiation in Scala using TypeTag and ClassTag

在Scala 2.9中,可以實現多態實例化

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

但是從2.10開始, Manifest正在被TypeTag取代,我不清楚如何用TypeTag實現類似的TypeTag 如果TypeTag版本保留了所有可用的類型信息,我更願意。

我知道上面只適用於不需要構造函數args的traits / classes,然后它並不總是有效,但它對我需要的東西運行良好。 如果我能做得更好,新的反射API會很棒。

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

如果你想支持傳遞args,這是我用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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM