如何在不丢失类型的情况下代理对hashmap的调用

时间:2012-02-02 01:10:28

标签: scala types

在下面的代码中,当我从hashmap直接得到它时,我得到了正确的属性类型(PropertyA)。

当我通过ClassAbstract中的get方法代理此调用时类型为PropertyAbstract[_ <: A]

有没有办法代理调用hashmap并保持正确的类型?

另一个问题是如何通过类型检查将对象添加到revs数组?

class A
class B extends A
class C extends A

abstract class PropertyAbstract[T] {
  val revs = new java.util.ArrayList[T]
}

class PropertyA extends PropertyAbstract[B]
class PropertyB extends PropertyAbstract[C]

abstract class ClassAbstract {
  val props: scala.collection.immutable.HashMap[String, PropertyAbstract[_ <: A]]
  def get(prop: String) = props.get(prop).get
}

class Class extends ClassAbstract {
  val props = collection.immutable.HashMap(
      "prop1" -> new PropertyA,
      "prop2" -> new PropertyB
  )
}

object Test extends App {
  val the_class = new Class
  val proxied_prop = the_class.get("prop1")
  val direct_prop =  the_class.props.get("prop1").get

  // wont compile (found: B     required: _$1 <: A)
  proxied_prop.revs.add(new B) 
  // wont compile (found: B     required: C with B)
  direct_prop.revs.add(new B)
}

我想要的结果是我可以向prop1添加B类型的元素,但不能添加C类型的元素

2 个答案:

答案 0 :(得分:3)

问题是get是在ClassAbstract中定义的,而道具是在Class中具体定义的,并且还有ClassAbstract无法访问的类型细化。我们需要提供一种方法,将这些附加类型信息从Class传递回ClassAbstract。以下是这样做的一种方法。

abstract class ClassAbstract[P <: PropertyAbstract[_ <: A]] {
  val props: scala.collection.immutable.HashMap[String,P]
  def get(prop: String) = props.get(prop)
}

class Class extends ClassAbstract[Property] {
  val props = collection.immutable.HashMap(
      "prop" -> new Property
  )
}

这使得直接get调用返回类型为Property的内容。

答案 1 :(得分:2)

看起来你想要的实际上是一个打字的地图。很明显,你要做的事情根本无法奏效。当您致电get时,您会收到PropertyAbstract[X]的未知X(除非是A的子类型)。你怎么能假设它需要B s?

解决方案是让你的PropertyAbstract成为逆变,但这意味着它不能以任何明智的方式成为可变集合(它可以是可变的当然,但你可以得到的是A)。

scala> class A; class B extends A; class C extends A
defined class A
defined class B
defined class C

scala> abstract class PropertyAbstract[-T] { val revs = new java.util.ArrayList[AnyRef] }
defined class PropertyAbstract

scala> class PropertyA extends PropertyAbstract[B]; class PropertyB extends PropertyAbstract[C]
defined class PropertyA
defined class PropertyB

scala> abstract class ClassAbstract {
   | val props: Map[String, PropertyAbstract[_ <: A]]
   | def get(prop: String) = (props get prop).get
   | }
defined class ClassAbstract

scala> class Class extends ClassAbstract { val props = Map("prop1" -> new PropertyA, "prop2" -> new PropertyB) }
defined class Class

scala>  val the_class = new Class
the_class: Class = Class@298508

scala> val proxied_prop = the_class.get("prop1")
proxied_prop: PropertyAbstract[_ <: A] = PropertyA@a269e2

scala> val direct_prop =  the_class.props.get("prop1").get
direct_prop: PropertyAbstract[C with B] = PropertyA@a269e2

以下编译:

scala> proxied_prop.revs.add(new B)
res0: Boolean = true

scala> direct_prop.revs.add(new B)
res1: Boolean = true

但当然你可以放任何东西!

也许你应该看一下Miles Sabin's shapeless你可以 在非异类型集合方面做的事情。