制作通用函数并在其上使用TypeOf

时间:2013-12-09 15:33:07

标签: scala generics reflection introspection

我有一个母亲特质'合约',由一些案例类扩展,'scale'就是其中之一。我试图创建一个泛型函数,使得它接受其中一个case类的对象,并根据它是什么类型的对象做一些动作。这是部分代码:

 def getType[T: TypeTag](obj: T) = typeOf[T]

 def makeXMLElem[T <: Contract](contract: T): String = {
      println(getType(contract))
      val symb = getType(contract).declaration(newTermName(name)).asTerm
      val instanceMirror = runtimeMirror(contract.getClass.getClassLoader).reflect(contract)
      val symbMirror = instanceMirror.reflectField(symb)
      val symbValue = symbMirror.get
      .......

现在,我尝试传递'scale'并检查其类型。 getType函数将其类型返回为“Contract”而不是“scale”。您可以理解,我正在尝试使用以下语句访问案例类'scale'的参数:

val symb = getType(contract).declaration(newTermName(name)).asTerm

案例类'scale'具有以下签名:

case class scale(amount:Int, currency:String)

因为,类型本身被错误地提取,值'symb'没有给出任何值,我得到以下运行时错误:

Caused by: scala.ScalaReflectionException: <none> is not a term

如何使函数makeXMLElem更通用,而不会丢失有关“scale”签名或任何扩展“Contract”的类的信息?

1 个答案:

答案 0 :(得分:2)

正如您从getType的定义中看到的那样,typeOf[T]函数实际上并不关心您的值obj。它只是在编译时,为您提供类型T的具体表示。因此,如果您有trait Foo; trait Bar extends Foo; getType[Foo](new Bar {}),则会获得Foo而不是Bar的类型信息。具体化的泛型的重点是在编译时保留必要的信息。

解决方案位于this answer:您必须通过contract使用getClass运行时类并反映该结果。

def getType[T](clazz: Class[T])(implicit rm: ru.Mirror) =
  rm.classSymbol(clazz).toType

val symb = getType(contract.getClass)

因此,您的方法签名可以简化为def makeXMLElem(contract: Constract)


示例:

import reflect.runtime.{universe => ru}
import ru._
implicit val rm = runtimeMirror(getClass.getClassLoader)

trait Foo; case class Bar(baz: Int) extends Foo

val b: Foo = Bar(33)
b.getClass // Bar!

val symb           = getType(b.getClass).declaration(newTermName("baz")).asTerm
val instanceMirror = rm.reflect(b)
val symbMirror     = instanceMirror.reflectField(symb)
symbMirror.get  // 33 !