【发布时间】:2011-09-14 08:04:29
【问题描述】:
我想按照以下代码从类标记映射到实例:
trait Instances {
def put[T](key: Class[T], value: T)
def get[T](key: Class[T]): T
}
这可以在不需要解析 get 方法中的强制转换的情况下完成吗?
更新:
对于使用Foo[T] 而不是Class[T] 的更一般的情况,如何做到这一点?
【问题讨论】:
标签: scala
我想按照以下代码从类标记映射到实例:
trait Instances {
def put[T](key: Class[T], value: T)
def get[T](key: Class[T]): T
}
这可以在不需要解析 get 方法中的强制转换的情况下完成吗?
更新:
对于使用Foo[T] 而不是Class[T] 的更一般的情况,如何做到这一点?
【问题讨论】:
标签: scala
您可以尝试以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))
}
【讨论】:
key.cast(obj) 在这里所做的实际上是经过检查的 (T) obj 演员表。 cast 方法也会抛出 ClassCastException 如果你给它的东西不是T 的实例。
在我朋友的帮助下,我们将带有键的映射定义为 Manifest 而不是 Class,这在调用时提供了更好的 api。
我没有收到您关于“使用 Foo[T] 而不是 Class[T] 的一般情况”的更新问题。但这应该适用于您指定的情况。
对象实例{ 私有 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] ) { 放(1) 放(“2”) println(get[Int]) 打印(获取[字符串]) } }【讨论】:
如果您想在没有 any 转换的情况下执行此操作(即使在 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 是一个确定的不变类型,那么您应该避免这种情况。它仍然没有解决统一问题,但至少它是健全的。不幸的是,类型构造函数被假定为处于 co-、contra- 和不变性之间的神奇叠加位置,所以如果它真的是任意类型 Foo 类型 * => *,那么你就是仍然沉没在存在主义的前沿。
总而言之:这应该是可能的,但前提是您将Instances 完全编码为HMap。就我个人而言,我只会在get 内投。简单多了!
【讨论】: