Scala集合中的类型化容器

时间:2017-03-19 22:51:51

标签: scala

基本上,我需要异构地图,但我知道这不容易实现。

trait Box[T] {
    def exec(): T
}
class IntBox extends Box[Int] {
    def exec() = 69
}
class StringBox extends Box[String] {
    def exec() = "foo"
}

因此,当我将Box子类实例放入Map时,由于JVM的类型擦除,相关的类型参数就消失了。

val map = Map(0 -> new IntBox, 1 -> new StringBox)
val i: Any = map(0).exec() // compiles but not type safe and requires a cast later on
val ii: Int = map(0).exec() // compile error :(
// would work but in general it is not known at this point that type 'Int' is required
val iii: Int = map(0).exec().asInstanceOf[Int] 

所以,问题是如何定义类型安全的 get 操作。请。请注意,Box子类是由代码的最终用户提供的,因此,我无法控制存在多少Boxes(因此,使用TypeTags的个别匹配不是可能的)。

到目前为止,我试图找到一个TypeTags的通用解决方案,但没有成功(通过向Box添加类型标记字段,但由于类型参数也被删除,我没有获得任何东西)。我很高兴修改exec(或添加一个包装器),以便最终(一个总是成功的)演员可以完成 - 但我不确定如何获得正确的类型信息点。我也很乐意使用像Shapeless这样的库。 (但是,我很怀疑我是否可以使用无形hmap,因为我无法提供所有可能的转换作为预先提示。我还发现了一些类型信息存储在密钥中的示例,但这对我的情况没有帮助,因为当我能够以类型安全的方式从另一个数据结构获取密钥时,我也可以为首先是价值观。任何提示?

1 个答案:

答案 0 :(得分:0)

我们讨论过这个,但为了完整起见,我的回答是:

import scala.util.Try

class AnyMap(map: Map[Int, Any] = Map.empty) {
  def add[A](int: Int, a: A): AnyMap =
    new AnyMap(map + (int -> (a: Any)))

  def tryGet[A](int: Int): Try[A] = Try(map(int).asInstanceOf[A])
}

这只是向上转换所有值并将它们推向同一个值 Map[Int, Any],然后在检索时向下转发。

使用示例:

val anyMap = new AnyMap
anyMap.add(0, 1).add(1, true).tryGet[Boolean](1)

输出:

Try[Boolean] = Success(true)

我认为这是相当合理的,因为我们已经习惯了 在Scala中传递类型参数的各种地方;不得不 传递参数实际上应该作为一个有用的提醒,我们是 在这里做一些与众不同的事情。事实上,多亏了类型 推理有时我们可以指定整个表达式的返回类型 并避免将显式类型参数传递给方法调用:

val tryValue: Try[Boolean] = anyMap.add(0, 1).add(1, true).tryGet(1) 

输出:

tryValue: Try[Boolean] = Success(true)