7
votes

Given a type declaration, I am able to resolve the type argument.

scala> reflect.runtime.universe.typeOf[List[Int]] match {case x:TypeRef => x.args}
res10: List[reflect.runtime.universe.Type] = List(Int)

For a runtime value, The same method doesn't work.

scala> reflect.runtime.currentMirror.reflect(List(42)).symbol.toType match {case x:TypeRef => x.args}
res11: List[reflect.runtime.universe.Type] = List(B)

Is there a way to overcome the type erasure for reflected values?

1
Maybe you can find a viable solution after reading this answer - Raffaele
@Raffaele. Thanks for the reference. Unfortunately the question (and the answer) deal with the case when the types are concrete and are known during compile time. Manifests are deprecated now, and TypeTags (which replaced manifests) require concrete tags. - Sagie Davidovich
Use AbsTypeTag (in RC1 renamed to WeakTypeTag). More info here: stackoverflow.com/questions/12218641/… - Eugene Burmako

1 Answers

6
votes

An example based on TypeTag knowledge gained from reading Scala: What is a TypeTag and how do I use it? posted by Eugene Burmako in the comments on your question:

import scala.reflect.runtime.universe._

object ScalaApplication {
  def main(args: Array[String]) {
    printType(List(42))
    printType(List("42"))
    printType(List("42", 42))
  }    

  def printType[T : TypeTag](t: T) {
    println(typeOf[T])
  }
}

This should give the output:

$ scala ScalaApplication.scala 
List[Int]
List[String]
List[Any]

[UPDATE 1:]

However, if you want to be aware of the type assigned to a reference of type Any you might have to opt for some sort of type aware wrapper:

import scala.reflect.runtime.universe._

object ScalaApplication {
  def main(args: Array[String]) {
    val anyWrapper = new AnyWrapper

    List(1,2,3).foreach { i =>
      i match {
        case 1 => anyWrapper.any = 42
        case 2 => anyWrapper.any = "a string"
        case 3 => anyWrapper.any = true
      }
      print(anyWrapper.any)
      print(" has type ")
      println(anyWrapper.typeOfAny)
    }
  }

  class AnyWrapper {
    private var _any: Any = null
    private var _typeOfAny: Type = null

    def any = _any
    def typeOfAny = _typeOfAny
    def any_=[T: TypeTag](a: T) = {
      _typeOfAny = typeOf[T]
      _any = a
    }

  }
}

This should give the output:

$ scala ScalaApplication.scala 
42 has type Int
a string has type String
true has type Boolean

But this solution still does not cover the case where the reference type is unknown at compile time.

[UPDATE 2:]

If the types are explicitly cast to reference of type Any, you might have to enumerate all the possible types in a match statement in order to recover the type:

import scala.reflect.runtime.universe._

object ScalaApplication {
  def main(args: Array[String]) {

    List(1,2,3).foreach { i =>
      val any: Any = i match {
        case 1 => 42.asInstanceOf[Any]
        case 2 => "a string".asInstanceOf[Any]
        case 3 => true.asInstanceOf[Any]
      }
      print(any)
      print(" has type ")
      println(matchType(any))
    }
  }

  def matchType(any: Any) = {
    any match {
      case a: Int => typeOf[Int]
      case a: String => typeOf[String]
      case a: Boolean => typeOf[Boolean]
    }
  }
}

This should give the output:

$ scala ScalaApplication.scala
42 has type Int
a string has type String
true has type Boolean

But this solution requires you to know (and list) all the possible types that you could receive in the any value.