在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]
}
答案 0 :(得分:4)
首先,重要的是要了解变量的类型与存储在该变量中的值的类型之间存在差异。在您的示例中,结果++
的实际类型仍然是PrefixMap
,但是变量的类型(即编译器可以证明的内容)仅为mutable.Map
,这就是您在REPL中看到的。您可以通过打印res2.getClass
以获取实际类型来轻松地进行验证。
我认为发生这种情况是因为Map
实际上有两种不同的++
方法:
一个来自TraversabelLike
,它是一个聪明的人,拥有CanBuildFrom
和所有其他奇特的东西
另一个来自scala.collection.MapLike
的{{1}},在scala.collection.mutable.MapLike
中被覆盖,它的通用性要差得多
// collection.MapLike so here Map is collection.Map
def ++[V1 >: V](xs: GenTraversableOnce[(K, V1)]): Map[K, V1]
// mutable.MapLike so here the output Map is the mutable.map
override def ++[V1 >: V](xs: GenTraversableOnce[(K, V1)]): Map[K, V1]
由于您的代码位于特定的上下文中,在该上下文中编译器确切地知道该类(与之类的通用.filter
类似,必须使用TraversableLike
基础结构),因此编译器使用更简单的{{ 1}}来自++
。
我不确定为什么其他mutable.MapLike
方法首先存在。可能是为了支持与某些旧式设计的向后兼容性(Scala集合库已重新设计了几次)。但是,使这一系列方法(还有更多类似MapLike.++
)工作的唯一方法是执行+
所做的事情:即在mutable.MapLike
定义中覆盖它们更具体的类型。另外请注意,PrefixMap
在内部使用MapLike.++
,因此最好使用它正确地使用或重新实现它。