【问题标题】:How to create an instance of type T at runtime with TypeTags如何在运行时使用 TypeTags 创建类型 T 的实例
【发布时间】:2018-07-06 10:47:03
【问题描述】:

下面是如何在运行时使用Manifest 创建一个类型为 T 的新实例:

trait MyTrait
class MyClass1(val name: String) extends MyTrait
class MyClass2(val name: String) extends MyTrait

class Test[T <: MyTrait] {

  def createInstance[T](name: String)(implicit m: Manifest[T]): T = {
    m.runtimeClass.getConstructors()(0)
      .newInstance(name).asInstanceOf[T]
  }

  def doSomething() {
    val myClass = createInstance("joe")
    ...
  }
}

...

val test = new Test[MyClass1]
test.doSomething

上面的createInstance 方法创建了一个实现MyTrait 的类的新实例,并使用给定的字符串调用构造函数。如何使用TypeTag 实现相同的功能?


使用 scala.reflect.runtime._ 重新实现。_

下面是按照 som-snytt 的建议重新实现的 Test 类:

class Test[T <: MyTrait] {

  import scala.reflect.runtime._
  import scala.reflect.runtime.universe._

  def createInstance[T: TypeTag](name: String): T = {
    val tt = typeTag[T]

    currentMirror.reflectClass(tt.tpe.typeSymbol.asClass).reflectConstructor(
      tt.tpe.members.filter(m =>
        m.isMethod && m.asMethod.isConstructor
      ).iterator.next.asMethod
    )(name).asInstanceOf[T]
  }   
}

只要我在Test 上调用createInstance 就可以了:

val test = new Test[MyClass1]
val myClass = test.createInstance("hello") // this works

但只要我定义了一个派生自 Test 的新类...

class DerivedTest[T <: MyTrait] extends Test[T]

...我像这样在那个新类上调用createInstance...

val test = new DerivedText[MyClass1]
val myClass = test.createInstance("hello") // this crashes

...我收到以下错误:

java.util.NoSuchElementException: next on empty iterator
    at scala.collection.Iterator$$anon$2.next(Iterator.scala:39)
    at scala.collection.Iterator$$anon$2.next(Iterator.scala:37)
    at scala.collection.LinearSeqLike$$anon$1.next(LinearSeqLike.scala:62)
    at DerivedTest$class.createInstance(<console>:27)
    at $anon$1.createInstance(<console>:26)
    at .<init>(<console>:28)
at .<clinit>(<console>)
    at .<init>(<console>:7)
    at .<clinit>(<console>)
    at $print(<console>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
...

我错过了什么吗?

【问题讨论】:

  • TcreateInstance 方法阴影TTest 类中。这可能不是你的意思。
  • 是的,@ghik 告诉过你不要隐藏 T。在这里,你向 ctor 提供了一个类型参数,而不是 createInstance。
  • 也许我遗漏了什么……但 createInstance 应该在“Test”类中​​创建 T 类型的实例。

标签: scala reflection


【解决方案1】:

最终这里是真正有效的实现 - 请假设这个答案来自 som-snytt

import scala.reflect.runtime._
import scala.reflect.runtime.universe._

class Test[T <: MyTrait : TypeTag] {

  def createInstance(args: AnyRef*)(ctor: Int = 0): T = {
    val tt = typeTag[T]

    currentMirror.reflectClass(tt.tpe.typeSymbol.asClass).reflectConstructor(
      tt.tpe.members.filter(m =>
        m.isMethod && m.asMethod.isConstructor
      ).iterator.toSeq(ctor).asMethod
    )(args: _*).asInstanceOf[T]
  }   
}

希望对你有帮助。

【讨论】:

  • 您只需要 Test 或 createInstance 上的类型参数。如果在测试中,则不需要 val tt,或者将其设为 def。 TypeTag 成为隐式类参数class Test[T]()(implicit x: TypeTag[T])。作为类型绑定,正如您所展示的,您可以隐式地[TypeTag[T]] 来获取它。
  • 是的……你说得对……我昨晚有点累。固定的。再次感谢您。
【解决方案2】:

也许一直这样做的人可以插话,但我花了这么多步骤来重现它。

我启动了 REPL -i,所以自动完成功能被破坏了。这让我处于劣势。

scala> class X(i: Int)
defined class X

scala> typeTag[X]
res0: reflect.runtime.universe.TypeTag[X] = TypeTag[X]

scala> .tpe
res1: reflect.runtime.universe.Type = X

scala> .members
res2: reflect.runtime.universe.MemberScope = Scopes(constructor X, value i, method $asInstanceOf, method $isInstanceOf, method synchronized, method ##, method !=, method ==, method ne, method eq, constructor Object, method notifyAll, method notify, method clone, method getClass, method hashCode, method toString, method equals, method wait, method wait, method wait, method finalize, method asInstanceOf, method isInstanceOf, method !=, method ==)

scala> res2.filter(s => s.isMethod && s.asMethod.isConstructor)
res4: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(constructor X, constructor Object)

scala> res4.iterator.next
res7: reflect.runtime.universe.Symbol = constructor X

scala> .typeSignature
res8: reflect.runtime.universe.Type = (i: scala.Int)X

scala> res7.asMethod
res11: reflect.runtime.universe.MethodSymbol = constructor X

scala> res1.typeSymbol.asClass
res13: reflect.runtime.universe.ClassSymbol = class X

scala> currentMirror reflectClass res13
res14: reflect.runtime.universe.ClassMirror = class mirror for X (bound to null)

scala> res14 reflectConstructor res11
res16: reflect.runtime.universe.MethodMirror = constructor mirror for X.<init>(i: scala.Int): X (bound to null)

scala> res16(7)
res17: Any = X@28730c5a

scala> .asInstanceOf[X]
res18: X = X@28730c5a

【讨论】:

  • 是的,目前这很冗长。将在issues.scala-lang.org/browse/SI-8221修复。
  • @EugeneBurmako 你总是比我们领先一步,所以猴子。有时分两步。
  • Tx... 只要我不从派生类调用 createInstance 方法,它就可以工作。如果我从派生类调用 createInstance,我总是在空迭代器上收到错误 java.util.NoSuchElementException: next 因为它找不到构造函数。
  • @j3d 如果我理解你的话,是的,你必须调用 createInstance[MyClass1] 以便它知道你在反映什么。你不能假设它在推断你想要的类型。
  • 我刚刚更新了我的帖子,提供了更多详细信息。有什么解决方法吗?
【解决方案3】:

j3d,

我相信清单方法已被弃用。 您可以使用下面的 scala 代码通过反射创建实例。 您将不得不稍微调整代码以考虑具有多个构造函数和/或参数的类

代码基于http://docs.scala-lang.org/overviews/reflection/overview.html

import scala.reflect.runtime.{universe => ru}
import ru._

...

class Person { }


def example() = 
{
   val instance1 = createInstance[Person]()
   val instance2 = createInstance(typeOf[Person])
}


def createInstance[T:TypeTag]() : Any= {
    createInstance(typeOf[T])
}


def createInstance(tpe:Type): Any = {
    val mirror = ru.runtimeMirror(getClass.getClassLoader)
    val clsSym = tpe.typeSymbol.asClass
    val clsMirror = mirror.reflectClass(clsSym)
    val ctorSym = tpe.decl(ru.termNames.CONSTRUCTOR).asMethod
    val ctorMirror = clsMirror.reflectConstructor(ctorSym)
    val instance = ctorMirror()
    return instance
}

【讨论】:

    【解决方案4】:
    object BeanFactory {
    
      import scala.reflect.runtime.universe._
      import scala.reflect.runtime.{universe => ru}
    
      def createBean[T: TypeTag](): Option[T] = {
        val typee = ru.typeOf[T]
        val constructor = typee.decl(ru.termNames.CONSTRUCTOR).asMethod
        if (constructor.isPrivate) {
          println("private class can not created ")
          None
        } else {
          val classMirror = ru.runtimeMirror(getClass.getClassLoader).reflectClass(typee.typeSymbol.asClass)
          val constructorMethod = classMirror.reflectConstructor(constructor)
          val params = constructor.paramLists.flatten.map(par => {
            if (par.typeSignature =:= typeOf[Int]) {
              0
            } else {
              if (par.typeSignature =:= typeOf[String]) {
                ""
              } else {
                if (par.typeSignature =:= typeOf[Double]) {
                  0.0
                } else {
                  if (par.typeSignature =:= typeOf[Float]) {
                    0.0f
                  } else {
                    if (par.typeSignature =:= typeOf[Char]) {
                      ""
                    } else {
                      if (par.typeSignature =:= typeOf[Boolean]) {
                        false
                      } else {
                        null
                      }
                    }
                  }
                }
              }
            }
    
          })
          Some(constructorMethod(params: _*).asInstanceOf[T])
        }
      }
    }
    

    这可能会导致您的问题

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多