I have a mother trait 'Contract' that is extended by a number of case classes, 'scale' being one of them. I am trying to make a generic function such that it takes an object of one of those case classes and does some action on the basis of what kind of object it is. This is the partial code:
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
.......
Now, I try to pass 'scale' and check its type. The getType function returns its type to be 'Contract' rather than 'scale'. As you can understand, I am trying to access the parameters of the case class 'scale' using the statement:
val symb = getType(contract).declaration(newTermName(name)).asTerm
case class 'scale' has the following signature:
case class scale(amount:Int, currency:String)
Since, the type itself is being extracted wrongly, the value 'symb' does not give any value and I get the following runtime error:
Caused by: scala.ScalaReflectionException: <none> is not a term
How can I make the function makeXMLElem more generic without the loss of information regarding the signature of 'scale' or any class that extends 'Contract' for that matter?
As you can see from your definition of getType
, The typeOf[T]
function doesn't actually care at all about your value obj
. All it does is, at compile time, give you a reified representation of type T
. So if you have trait Foo; trait Bar extends Foo; getType[Foo](new Bar {})
, you will get the type information for Foo
not Bar
. The whole point of reified generics is to preserve the necessary information at compile time.
The solution lies in this answer: You have to use the runtime class of contract
via getClass
and reflect that result.
def getType[T](clazz: Class[T])(implicit rm: ru.Mirror) =
rm.classSymbol(clazz).toType
val symb = getType(contract.getClass)
Consequently your method signature can be simplified to def makeXMLElem(contract: Constract)
.
Example:
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 !