【问题标题】:Polymorphic instantiation in Scala using TypeTag and ClassTag在 Scala 中使用 TypeTag 和 ClassTag 进行多态实例化
【发布时间】:2013-08-28 22:33:17
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: scala reflection scala-2.10


    【解决方案1】:

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

    【讨论】:

    • 谢谢@ghik,我仍然有兴趣了解如何使用TypeTag 来做这件事,最好同时保留完整的类型信息,即类型推断应该推断newInstance[MyClass{Int]] : MyClass[Int]
    • @DanielMahler 我添加了基于TypeTag 的解决方案。请查看我的编辑。
    • @ghik 当你使用runtimeMirror(this.getClass.getClassLoader) 时,当T 没有使用与this.type 相同的类加载器加载时,你会得到一个异常。您应该使用附加到TypeTag 的镜像:typeTag[T].mirror
    【解决方案2】:

    如果你想支持传递参数,这是我在 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2016-09-12
      • 2019-06-27
      • 1970-01-01
      • 1970-01-01
      • 2018-10-06
      • 1970-01-01
      相关资源
      最近更新 更多