我有一个母亲特质'合约',由一些案例类扩展,'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”的类的信息?
答案 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 !