Scala 为什么使用PrefixMap++;PrefixMap变为可变的.Map

Scala 为什么使用PrefixMap++;PrefixMap变为可变的.Map,scala,Scala,在sbt控制台中运行Scala中编程的PrefixMap示例 scala> PrefixMap("abc" -> 12, "abb" -> 13) res0: PrefixMap[Int] = Map(abc -> 12, abb -> 13) scala> PrefixMap("aaa" -> 15) res1: PrefixMap[Int] = Map(aaa -> 15) scala> res0 ++ res1 res2: scal

在sbt控制台中运行Scala中编程的PrefixMap示例

scala> PrefixMap("abc" -> 12, "abb" -> 13)
res0: PrefixMap[Int] = Map(abc -> 12, abb -> 13)

scala> PrefixMap("aaa" -> 15)
res1: PrefixMap[Int] = Map(aaa -> 15)

scala> res0 ++ res1
res2: scala.collection.mutable.Map[String,Int] = Map(abc -> 12, abb -> 13, aaa -> 15)
这个结果把我弄糊涂了

我认为当调用“++”方法时,“++”方法在对象PrefixMap中使用隐式canBuildFrom并创建新的PrefixMap实例,但它似乎在某处使用另一个隐式值并创建新的Map实例

为什么res2类型不是PrefixMap? 或者,我在某个地方犯了个错误

下面是PrefixMap示例中的代码

import collection._
import scala.collection.mutable.{Builder, MapBuilder}
import scala.collection.generic.CanBuildFrom

object PrefixMap {
  def empty[T] = new PrefixMap[T]

  def apply[T](kvs: (String, T)*): PrefixMap[T] = {
    val m: PrefixMap[T] = empty
    for (kv <- kvs)
      m += kv

    m
  }

  def newBuilder[T]: Builder[(String, T), PrefixMap[T]] =
    new MapBuilder[String, T, PrefixMap[T]](empty)

  implicit def canBuildFrom[T]
    : CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] =
      new CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] {
        def apply(from: PrefixMap[_]) = newBuilder[T]
        def apply() = newBuilder[T]
      }
}

class PrefixMap[T] extends mutable.Map[String, T]
  with mutable.MapLike[String, T, PrefixMap[T]] {
  var suffixes: immutable.Map[Char, PrefixMap[T]] = Map.empty
  var value: Option[T] = None

  def get(s: String): Option[T] =
    if(s.isEmpty) value
    else          suffixes get (s(0)) flatMap (_.get(s substring 1))

  def withPrefix(s: String): PrefixMap[T] = {
    if(s.isEmpty) this
    else {
      val leading = s(0)
      suffixes get leading match {
        case None => suffixes = suffixes + (leading -> empty)
        case _    =>
      }
      suffixes(leading) withPrefix (s substring 1)
    }
  }

  override def update(s: String, elem: T) =
    withPrefix(s).value = Some(elem)

  override def remove(s: String): Option[T] =
    if(s.isEmpty) { val prev = value; value = None; prev}
    else suffixes get (s(0)) flatMap (_.remove(s substring 1))

  def iterator: Iterator[(String, T)] =
    (for (v <- value.iterator) yield ("", v)) ++
      (for ((chr, m) <- suffixes.iterator;
            (s, v) <- m.iterator) yield (chr +: s, v))

  def += (kv: (String, T)): this.type = { update(kv._1, kv._2); this }
  def -= (s: String): this.type = { remove(s); this }

  override def empty = new PrefixMap[T]
}
导入集合_
导入scala.collection.mutable.{Builder,MapBuilder}
导入scala.collection.generic.CanBuildFrom
对象前缀映射{
def empty[T]=新的前缀映射[T]
def应用[T](kvs:(字符串,T)*):前缀映射[T]={
val m:PrefixMap[T]=空
对于(kv后缀=后缀+(前导->空)
案例=>
}
带前缀的后缀(前导)(s子字符串1)
}
}
覆盖def更新(s:String,elem:T)=
带前缀。值=一些(元素)
覆盖def删除(s:字符串):选项[T]=
如果(s.isEmpty){val prev=value;value=None;prev}
else后缀get(s(0))flatMap(u.remove(s子字符串1))
def迭代器:迭代器[(字符串,T)]=

(对于(v首先,重要的是要理解变量的类型和存储在该变量中的值的类型之间存在差异。在您的示例中,
++
结果的实际类型仍然是
PrefixMap
,但变量的类型(即编译器可以证明的)只是
mutable.Map
,这是您在REPL中看到的。您可以通过打印
res2.getClass
以获取实际类型来轻松验证这一点

我认为这是因为
Map
实际上有两种不同的
++
方法:

  • 一个来自
    TraversabelLike
    ,它是一个智能的,具有
    CanBuildFrom
    和所有其他奇特的功能

  • 另一个来自
    scala.collection.MapLike
    scala.collection.mutable.MapLike
    处被覆盖,它的通用性要小得多

//collection.MapLike所以这里的Map就是collection.Map
def++[V1>:V](xs:gentraversableone[(K,V1)]):Map[K,V1]
//因此,这里的输出映射是mutable.Map
覆盖def++[V1>:V](xs:gentraversableone[(K,V1)]):映射[K,V1]
由于您的代码位于特定的上下文中,编译器确切地知道类(而不是像泛型的
.filter
这样必须使用
TraversableLike
基础结构),因此编译器使用来自
mutable.MapLike
的更简单的
++


我不确定为什么会首先存在另一个
MapLike.++
方法。这可能是为了支持与某些遗留设计的向后兼容性(Scala collections library已经被重新设计了好几次)。但唯一的方法是创建这组方法(还有几个类似
+
)要想按您的意愿工作,就要做
mutable.MapLike
所做的事情:即在
PrefixMap
中覆盖它们,定义更具体的类型。还要注意
MapLike.++
内部使用
clone()
,因此您最好正确使用它或重新实现它。

PrefixMap
s是
mutable.Map
s。它看起来像是将其视为常见的超类型。在阅读了您的文章后,我验证了
+
方法是否调用
mutable.MapLike.++
。结果肯定满足了
+
的要求hod调用
mutable.MapLike.++
而不是调用
TraversableLike.++
方法,该方法具有隐式
CanBuildFrom
参数。谢谢您的回答。