【问题标题】:Map from Class[T] to T without casting从 Class[T] 映射到 T 而不进行强制转换
【发布时间】: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


    【解决方案1】:

    您可以尝试以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))        
    }
    

    【讨论】:

    • 令人印象深刻的功能,将尽快重构一些代码 :) 感谢 Jean-Philipe
    • 你无法避免在没有具体类型的情况下进行强制转换,但这种技术对我来说看起来真的很安全。
    • @michid 确实,你无法避免强制转换。 key.cast(obj) 在这里所做的实际上是经过检查的 (T) obj 演员表。 cast 方法也会抛出 ClassCastException 如果你给它的东西不是T 的实例。
    【解决方案2】:

    在我朋友的帮助下,我们将带有键的映射定义为 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]) 打印(获取[字符串]) } }

    【讨论】:

      【解决方案3】:

      如果您想在没有 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 内投。简单多了!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-07
        • 2017-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多