Question

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?

Was it helpful?

Solution

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 !
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top