You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何PrefixMap调用++方法后返回mutable.Map而非PrefixMap?

为什么++返回的是mutable.Map而不是PrefixMap?

你遇到的问题核心在于编译器没有选中你自定义的CanBuildFrom实例,而是默认使用了mutable.Map自带的CanBuildFrom。让我们一步步拆解原因和解决方案:

问题根源

你的CanBuildFrom定义是这样的:

implicit def canBuildFrom[T] : CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] = ...

这里的问题有两个关键点:

  1. PrefixMap是不变类型:你的PrefixMap[T]继承自不变的mutable.Map[String, T],因此PrefixMap[Int]和PrefixMap[_]之间没有子类型关系(对于不变类,C[Int]不是C[_]的子类型,反之亦然)。而CanBuildFrom的第一个类型参数From是逆变的,这意味着编译器无法将CanBuildFrom[PrefixMap[_], ...]匹配到实际需要的CanBuildFrom[PrefixMap[Int], ...]。
  2. mutable.Map的CanBuildFrom更"具体":由于PrefixMap[Int]是mutable.Map[String, Int]的子类,而CanBuildFrom的From参数是逆变的,CanBuildFrom[Map[String, Int], (String, Int), Map[String, Int]](来自mutable.Map)会被编译器认为比你的自定义CanBuildFrom更匹配,因此被优先选中。

解决方案

调整你的CanBuildFrom定义,让它的From参数支持任意类型参数的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)
  
  // 修改这里:添加类型参数S,让From类型为PrefixMap[S]
  implicit def canBuildFrom[S, T] : CanBuildFrom[PrefixMap[S], (String, T), PrefixMap[T]] = new CanBuildFrom[PrefixMap[S], (String, T), PrefixMap[T]] {
    def apply(from: PrefixMap[S]) = 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> 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: PrefixMap[Int] = Map(abc -> 12, abb -> 13, aaa -> 15)

这次res2的类型就是PrefixMap[Int]了!

补充说明

  • CanBuildFrom的逆变特性:CanBuildFrom[-From, -Elem, +To]中,From是逆变的,意味着如果A <: B,那么CanBuildFrom[B, ...] <: CanBuildFrom[A, ...]。修改后的CanBuildFrom[PrefixMap[S], ...]比CanBuildFrom[Map[String, S], ...]更具体(因为PrefixMap[S] <: Map[String, S]),所以编译器会优先选择你的自定义实现。
  • 为什么原来的PrefixMap[_]不行:对于不变的PrefixMap[T],PrefixMap[_]是一个存在类型,它不代表任何具体的PrefixMap子类型,因此无法匹配实际的PrefixMap[Int]作为From参数。

内容的提问来源于stack exchange,提问作者Mizunashi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:06:24