我在Scala中编写简单的变量系统。我需要一种方法来很好地保存这些AnyVar
并通过它们的字符串名称访问它们。我有这个对象称为上下文来管理它,但它实际上只是HashMap[String, AnyVar]
的包装器。我想找到一个更好的方法来做到这一点。
class Context {
import scala.collection.mutable.HashMap
import scala.reflect.ClassTag
private var variables = HashMap[String, AnyVal] ()
def getVariable (name: String):AnyVal = variables(name)
def setVariable (name: String, value: AnyVal) = variables += ((name, value))
def getVariableOfType[T <: AnyVal : ClassTag] (name:String):T ={
val v = variables(name)
v match {
case x: T => x
case _ => null.asInstanceOf[T]
}
}
}
我真的想要一个类型安全的实现
答案 0 :(得分:4)
您将变量的上限定义为AnyVal
。这些大多是原始类型,例如Int
,Boolean
等,而不是null
有意义的引用类型。因此,您必须将演员表添加到T
。这不会为您提供空引用,但会为您提供该值类型的默认值,例如0
为Int
,false
为Boolean
:
null.asInstanceOf[Int] // 0
null.asInstanceOf[Boolean] // false
因此,这与类型安全无关,而是Scala否则会拒绝在此处使用null
值这一事实。与x: T
匹配的模式已经是类型安全的。
如果您尝试查询不存在的变量或错误的类型,更好的方法是返回Option[T]
或抛出NoSuchElementException
。
请注意,使用没有同步的普通哈希映射意味着您的Context
不是线程安全的。
答案 1 :(得分:2)
使用抽象类型进行模式匹配很困难,对于AnyVal
的实例更是如此。我发现了一种适用于ClassTag based pattern matching fails for primitives
一般来说,对于您的问题,我只会使用带有隐式扩展名的Map[String, AnyVal]
来为您提供getOfType
。然后,您可以使用现有方法根据需要放置和获取变量。您可以使用类型别名为您的类型提供更有意义的参考。
object Context {
import scala.reflect.ClassTag
import scala.runtime.ScalaRunTime
type Context = Map[String, AnyVal]
def apply(items: (String, AnyVal)*): Context = items.toMap
implicit class ContextMethods(c: Context) {
class MyTag[A](val t: ClassTag[A]) extends ClassTag[A] {
override def runtimeClass = t.runtimeClass
override def unapply(x: Any): Option[A] =
if (t.runtimeClass.isPrimitive && (ScalaRunTime isAnyVal x) &&
(x.getClass getField "TYPE" get null) == t.runtimeClass)
Some(x.asInstanceOf[A])
else t unapply x
}
def getOfType[T <: AnyVal](key: String)(implicit t: ClassTag[T]): Option[T] = {
implicit val u = new MyTag(t)
c.get(key).flatMap {
case x: T => Some(x: T)
case _ => None
}
}
}
}
将这些东西收集在一个可以导入的对象中,使用它变得简单明了:
import Context._
val context = Context(
"int_var" -> 123,
"long_var" -> 456l,
"double_var" -> 23.5d
)
context.get("int_var")//Some(123)
context.get("long_var")//Some(456)
context.getOfType[Int]("int_var")//Some(123)
context.getOfType[Double]("double_var")//Some(23.5)
context.getOfType[Int]("double_var")//None