Map我正在尝试编写一个模板函数,该函数从基于类型中检索值。如果模板类型与 中的类型不匹配Map,则应返回None:
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
Run Code Online (Sandbox Code Playgroud)
这是行不通的,我尝试过的数百种变体也行不通。知道我怎样才能做到这一点吗?
您正在混合类和类型的定义。getClass返回类元数据。ATypeTag包含类型元数据。它们是不同的东西。例如,String, List,Map是类,而List[Int]和Map[Int, String]是类型。一般来说,将类与类型进行比较是没有意义的,因为您可能会将类似的内容List与 a进行比较List[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)
Run Code Online (Sandbox Code Playgroud)
但对于具有类型参数的类来说,这可能会失败。例如,如果您将 的定义更改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!!
Run Code Online (Sandbox Code Playgroud)
的类型参数List被删除。TypeTag将允许您保存所有类型信息,但是,一旦您的数据存储在 a 中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))
Run Code Online (Sandbox Code Playgroud)
这很笨重,但在使用非类型化Map. 根据您的要求,您可能需要研究诸如“shapeless” HMap之类的东西来确保类型安全。