从Class [T]映射到T而不进行强制转换

时间:2011-09-13 11:47:21

标签: scala

我想从类标记映射到以下代码行的实例:

trait Instances {
  def put[T](key: Class[T], value: T)
  def get[T](key: Class[T]): T
}

这可以在不必解析get方法中的强制转换的情况下完成吗?

更新

对于某些Foo[T]而不是Class[T]的更一般情况,如何做到这一点?

3 个答案:

答案 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内进行投射。更简单!