Reputation: 13078
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
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