使用ClassTag查看我拥有的是否与模板类型相同

thr*_*eez 2 scala

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)

这是行不通的,我尝试过的数百种变体也行不通。知道我怎样才能做到这一点吗?

Mic*_*jac 5

您正在混合类和类型的定义。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之类的东西来确保类型安全。