我想从类标记映射到以下代码行的实例:
trait Instances {
def put[T](key: Class[T], value: T)
def get[T](key: Class[T]): T
}
Run Code Online (Sandbox Code Playgroud)
这可以在不必解析get方法中的强制转换的情况下完成吗?
更新:
对于更普遍的情况,如何用一些Foo[T]而不是Class[T]?
您可以尝试从地图中检索对象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))
}
Run Code Online (Sandbox Code Playgroud)
在我的一个朋友的帮助下,我们将键定义为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])
}
}
如果您想在不进行任何转换(即使在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
Run Code Online (Sandbox Code Playgroud)
在此示例中,finalResult将是 类型Any。这实际上是正确的,因为子类型意味着我们对A. 这不是编译器选择该类型的原因,但它是一个正确的选择。举个例子:
val xs: List[(Class[A], A) forSome { type A }] = List(classOf[Boolean] -> 'bippy)
Run Code Online (Sandbox Code Playgroud)
这是完全合法的!子类型化意味着A在这种情况下将被选择为Any。这不是我们想要的,但这是您将得到的。因此,为了表达此约束而不跟踪所有单独的类型(使用HMap),Scala 需要能够表达类型是特定类型而不是其他类型的约束。不幸的是,Scala 没有这种能力,所以我们基本上停留在通用约束方面。
更新 实际上,这是不合法的。刚刚尝试了一下,编译器将其踢出。我认为这之所以有效,是因为Class它的类型参数是不变的。因此,如果Foo是一个不变的确定类型,那么您应该不会出现这种情况。它仍然没有解决统一问题,但至少是合理的。不幸的是,类型构造函数被认为处于同、反和不变性之间的神奇叠加,因此,如果它确实是任意类型Foo的 kind * => *,那么您仍然会陷入存在主义的困境。
总之:这应该是可能的,但前提是您完全编码Instances为HMap. 就我个人而言,我只会在里面投射get。简单多了!
| 归档时间: |
|
| 查看次数: |
573 次 |
| 最近记录: |