Scala:使用具体类型实现Map

Dob*_*eer 4 types scala

我在Scala类型系统中遇到了某种怪癖,让我有点难过.我正在尝试创建一个扩展Map [String,String]的类,我无法弄清楚如何以编译器接受它的方式实现+方法.

这是我现在的代码:

class ParamMap(val pairs:List[(String,String)] = Nil) extends Map[String,String] {

  lazy val keyLookup = Map() ++ pairs

  override def get(key: String): Option[String] = keyLookup.get(key)
  override def iterator: Iterator[(String, String)] = pairs.reverseIterator

  /**
   * Add a key/value pair to the map
   */
  override def + [B1 >: String](kv: (String, B1)) = new ParamMap(kv :: pairs)

  /**
   * Remove all values for the given key from the map
   */
  override def -(key: String): ParamMap  = new ParamMap(pairs.filterNot(_._1 == key))

  /**
   * Remove a specific pair from the map
   */
  def -(kv: (String, String)) : ParamMap = new ParamMap(pairs - kv)
}
Run Code Online (Sandbox Code Playgroud)

斯卡拉告诉我这个:

type mismatch;  found: (String, B1)  required: (String, String)
Run Code Online (Sandbox Code Playgroud)

我相信这是因为允许B1是String的子类型,但我的构造函数只需要一个String(?).我最初的尝试是:

override def +(kv: (String, String)) = new ParamMap(kv :: pairs)
Run Code Online (Sandbox Code Playgroud)

但这引起了抱怨,因为类型签名与特征不匹配:

class ParamMap needs to be abstract, since method + in trait Map of type [B1 >: String](kv: (String, B1))scala.collection.immutable.Map[String,B1] is not defined
method + overrides nothing
Run Code Online (Sandbox Code Playgroud)

我是Scala的新手,我想我已经在类型系统的工作方式上超越了我的头脑.也许我会尝试搞砸铸造,但我觉得可能有一种"更好的方法",如果我知道的话,将来会给我带来很多麻烦.

有任何想法吗?

Kip*_*ros 7

关于Scala类型系统的一些背景知识.

  • 语法B1 >: String意味着B1String.所以B1不太具体,也不能投String.相反,B1 <: String将是一种子类型关系.

  • 特征的定义MapMap [A, +B],其中A表示键B的类型和值的类型.该+B符号表示,Map协变的密钥类型,这意味着T <: S暗示Map[A, T] <: Map[A, S].

  • Map.+方法的完整类型是+ [B1 >: B] (kv: (A, B1)): Map[A, B1].使用的B那种力量的协方差B1 >: B.下面是一个如何工作的示例:给定一个地图m: Map[String, String]添加一个具有较少特定类型的键值对kv : (String, Any)将导致一个不太具体的地图,(m + kv): Map[String, Any].

最后一点说明了您的ParamMap定义问题.根据Map界面,人们应该能够将类型的键添加到类型Any的地图中ParamMap <: Map[String, String]并返回a Map[String, Any].但是你试图定义ParamMap.+总是返回ParamMap[String, String],这是不相容的Map.+.

解决问题的一种方法是提供ParamMap一个显式类型参数,例如(警告未经测试),

class ParamMap[B](val pairs:List[(String,String)] = Nil) extends Map[String, B] {
  ...
  override def + [B1 >: B](kv: (String, B1)) = new ParamMap[B1](kv :: pairs)
}
Run Code Online (Sandbox Code Playgroud)

但这可能不是你想要的.我认为没有办法将值类型修复为String并实现Map[String, String]接口.


鉴于以上所有,为什么你的答案中的代码会编译?您实际上发现了Scala模式匹配的限制(不健全),它可能导致运行时崩溃.这是一个简化的例子:

def foo [B1 >: String](x: B1): Int = {
  val (s1: Int, s2: Int) = (x, x)
  s1
}
Run Code Online (Sandbox Code Playgroud)

虽然这个编译,但它没有做任何有用的事情.事实上,它总会崩溃MatchError:

scala> foo("hello")
scala.MatchError: (hello,hello) (of class scala.Tuple2)
    at .foo(<console>:9)
    at .<init>(<console>:10)
    at .<clinit>(<console>)
    ...
Run Code Online (Sandbox Code Playgroud)

在你的回答中,你基本上告诉编译器将B1实例转换为a String,如果转换不起作用,你将会遇到运行时崩溃.这相当于一个不安全的演员,

(value: B1).asInstanceOf[String]
Run Code Online (Sandbox Code Playgroud)