bbarker
bbarker

Reputation: 13078

How to recover type at runtime using typetags?

My understanding is that TypeTags can allow us to recover types at runtime. However, I am unable to do so when using a collection parameterized with TypeTag[_]:

Here is my first attempt:

import scala.reflect.runtime.universe._
import scala.collection.mutable

type TaggedVal = Tuple2[TypeTag[_], Any]
val tagMap: Map[Int, TaggedVal] = Map(
  0 -> (typeTag[Int], 12345),
  1 -> (typeTag[String], "abcde")
)


def get[T](key: Int)(implicit tag: TypeTag[T]) = tagMap.get(key) match {
  case Some((t, v)) => Some(v.asInstanceOf[T])
  case _ => None
}

implicit val zeroTag = tagMap(0)._1
val zero = get(0).get
val testing: Int = zero

This results in failure to get back the stored type:

Error:(18, 26) type mismatch;
 found   : Any
 required: Int
lazy val testing: Int = zero
                    ^

A second similar attempt is the following, but it results in a match error:

type TypedVal = Tuple2[Type, Any]
val tagMap: mutable.Map[Int, TypedVal] = mutable.Map()

def put[T: TypeTag](key: Int, value: T) = tagMap.put(key, (typeOf[T], value))

def get[T: TypeTag](key: Int) = tagMap.get(key).get match {
  case tv: TypedVal if tv._1 =:= typeOf[T] => Some(tv._2.asInstanceOf[T])
}

put(0, (typeOf[Int], 12345))
put(1, (typeOf[String], "abcde"))
val zero = get(0).get

Is there any reasonable way to do this?

Reference: How save a TypeTag and then use it later to reattach the type to an Any (Scala 2.10)

Upvotes: 1

Views: 192

Answers (1)

Michael Zajac
Michael Zajac

Reputation: 55569

You need to supply the type parameter to get yourself, or else it can't work. You're essentially asking for the type at compile-time, when it is only known at run-time.

type TaggedVal = (TypeTag[_], Any)

val tagMap: Map[Int, TaggedVal] = Map(
  0 -> (typeTag[Int], 12345),
  1 -> (typeTag[String], "abcde")
)

def get[T](key: Int)(implicit tag: TypeTag[T]): Option[T] = tagMap.get(key) match {
  case Some((t, v)) if t.tpe =:= typeOf[T] => Some(v.asInstanceOf[T])
  case _ => None
}

scala> get[String](1)
res31: Option[String] = Some(abcde)

scala> get[Int](1)
res32: Option[Int] = None

Something like the following (without forcing the type parameter), however, cannot work, because the type that is returned (via a cast) is only known at run-time. i can't be inferred as an Int.

val i = get(0).get
val j: Int = i

Upvotes: 1

Related Questions