我想从类标记映射到以下代码行的实例:
trait Instances {
def put[T](key: Class[T], value: T)
def get[T](key: Class[T]): T
}
这可以在不必解析get方法中的强制转换的情况下完成吗?
更新
对于某些Foo[T]
而不是Class[T]
的更一般情况,如何做到这一点?
答案 0 :(得分:9)
您可以尝试从地图中检索Any
对象,然后使用Class[T]
“反复投射”:
trait Instances {
private val map = collection.mutable.Map[Class[_], Any]()
def put[T](key: Class[T], value: T) { map += (key -> value) }
def get[T](key: Class[T]): T = key.cast(map(key))
}
答案 1 :(得分:5)
在我的一位朋友的帮助下,我们将键定义为Manifest而不是Class,这样在调用时会给出更好的api。
我没有得到关于“一般情况下有些Foo [T]而不是Class [T]”的更新问题。但这适用于您指定的案例。
object Instances { private val map = collection.mutable.Map[Manifest[_], Any]() def put[T: Manifest](value: T) = map += manifest[T] -> value def get[T: Manifest]: T = map(manifest[T]).asInstanceOf[T] def main (args: Array[String] ) { put(1) put("2") println(get[Int]) println(get[String]) } }
答案 2 :(得分:2)
如果你想在没有任何投射的情况下做到这一点(即使在get
内),那么你需要编写一个异构地图。由于显而易见的原因,这很棘手。 :-)最简单的方法可能是使用类似HList
的结构并构建find
函数。但是,这并不简单,因为您需要定义一些检查两种任意类型的类型相等的方法。
我试图让元组和存在类型变得有点棘手。但是,Scala不提供统一机制(模式匹配不起作用)。此外,子类型将整个事物打结,基本上消除了它可能提供的任何安全性:
val xs: List[(Class[A], A) forSome { type A }] = List(
classOf[String] -> "foo", classOf[Int] -> 42)
val search = classOf[String]
val finalResult = xs collect { case (`search`, result) => result } headOption
在此示例中,finalResult
的类型为Any
。这实际上是正确的,因为子类型意味着我们并不真正知道关于A
的任何。这不是编译器选择该类型的原因,但它是一个正确的选择。举个例子:
val xs: List[(Class[A], A) forSome { type A }] = List(classOf[Boolean] -> 'bippy)
这完全合法!子类型意味着在这种情况下 A
将被选为Any
。这不是我们想要的,但它是你会得到的。因此,为了表达此约束而不跟踪所有类型的个体(使用HMap
),Scala需要能够表达类型为的约束特定的类型,没有别的。不幸的是,Scala没有这种能力,所以我们基本上都停留在通用约束前面。
更新实际上,这不合法。刚试了一下,编译器就把它踢出去了。我认为这只有效,因为Class
在其类型参数中是不变的。因此,如果Foo
是一个不变的确定类型,那么你应该对这种情况安全。它仍然无法解决统一问题,但至少它是合理的。不幸的是,假设类型构造函数处于共同,反对和不变性之间的神奇超级位置,因此如果它真正类Foo
的任意类型* => *
,然后你仍然沉没在存在主义的前线。
总结:它应该是可能的,但前提是您将Instances
完全编码为HMap
。就个人而言,我只会在get
内进行投射。更简单!