2016-05-11 22 views
1

我想写一个模板函数从基于类型的地图检索值。如果模板类型不匹配的地图类型,它应该返回无:使用ClassTag来看看我有什么是相同的模板类型

val validMetadata: Map[String, Any] = Map(
    "string" -> "this is a string", 
    "int" -> 12, 
    "double" -> 12.12 
) 

import scala.reflect.runtime.universe._ 
private def getMetadata[T](key: String)(implicit tag: TypeTag[T]): Option[T] = 
    validMetadata.get(key) match { 
    case Some(scalar) if scalar.getClass == tag.tpe => Option(scalar) 
    case _ => None 
    } 

getMetadata[Int]("int") // should return Option(12) 
getMetadata[Int]("string") // should return None 

这不起作用,而且也没有任何其他的几百个变化的我试过的。任何想法我可以做到这一点?

+0

是的。更新了原来的问题。感谢您指出了这一点。 – threejeez

回答

3

你在混合类和类型的定义。 getClass返回类元数据。 A TypeTag包含类型元数据。他们是不同的东西。例如,StringList,Map是类别,但List[Int]Map[Int, String]是类型。例如,将一个类与一个类型进行比较通常没有意义,因为您可能会比较ListList[Int]之类的内容。

你可以把它工作中使用ClassTag,它的提取:

import scala.reflect.ClassTag 

val validMetadata: Map[String, Any] = Map(
    "string" -> "this is a string", 
    "int" -> 12, 
    "double" -> 12.12 
) 

def getMetadata[T](key: String)(implicit tag: ClassTag[T]): Option[T] = 
    validMetadata.get(key) match { 
    case Some(tag(scalar)) => Option(scalar) 
    case _ => None 
    } 

scala> getMetadata[Int]("int") 
res1: Option[Int] = Some(12) 

scala> getMetadata[Int]("string") 
res2: Option[Int] = None 

scala> getMetadata[String]("string") 
res3: Option[String] = Some(this is a string) 

但是,这可能会因使用类型参数的类。例如,如果你改变validMetadata的定义:

val validMetadata: Map[String, Any] = Map(
    "ints" -> List(1, 2, 3), 
    "strings" -> List("a", "b", "c") 
) 

scala> getMetadata[List[String]]("ints") 
res5: Option[List[String]] = Some(List(1, 2, 3)) // No!! 

List类型参数被擦除。 TypeTag将允许您保存所有类型的信息,但是,一旦您的数据存储在Map[String, Any]中,所有类型的信息都将丢失,因此TypeTag无法提供帮助。为了使用它,你就必须从根本上您的Map更改为类似:

import scala.reflect.runtime.universe._ 

case class Tagged[A](value: A)(implicit val tag: TypeTag[A]) 

val validMetadata: Map[String, Tagged[_]] = Map(
    "ints" -> Tagged(List(1, 2, 3)), // Allows us to save type information at compile time, to carry over to run time 
    "strings" -> Tagged(List("a", "b", "c")) 
) 

def getMetadata[T](key: String)(implicit tag: TypeTag[T]): Option[T] = 
    validMetadata.get(key) match { 
    case Some(tagged) if(tag.tpe =:= tagged.tag.tpe) => Option(tagged.value.asInstanceOf[T]) 
    case _ => None 
    } 

scala> getMetadata[List[String]]("ints") 
res6: Option[List[String]] = None 

scala> getMetadata[List[String]]("strings") 
res7: Option[List[String]] = Some(List(a, b, c)) 

这是笨重,但是不受其他可能使用非类型化Map时恢复该深度的类型信息。根据您的要求,您可能希望看看类似shapelessHMap的类型安全。

+0

谢谢你这个惊人的描述! – threejeez

相关问题