2

给定 Scala 中的 Map[String, Int],我想创建一个特征,允许我向 add 和 get 方法添加额外的逻辑。这样做的正确语法是什么?(注意:我也想在每种情况下调用 super 方法)

我试过类似的东西:

var mymap: Map[String, Int] with mytrait[String, Int] = Map[String, Int]()

trait mytrait[A, B] {

    abstract override def +[ B1 >: B] (kv: (A, B1)) : Map[A, B1] = { /* ... */ }

}

但是解释器抱怨所以我显然在语法上遗漏了一些东西,特别是在使用类型参数时。

非常感谢您的帮助

更具体地说:我所拥有的是我的代码中已经存在的映射,因此我不想重写我的程序的大块,而是想向 Map 的 + 方法和 get 方法添加逻辑,以便它执行额外的逻辑每次将项目添加到程序中其他地方的地图时。因此,为什么我选择了一个 trait 来为地图添加功能

这是我目前的代码:

    trait RegManager[A, B] extends scala.collection.Map[A, B] {
    case class KeyAlreadyExistsException(e: String) extends Exception(e)

    abstract override def + [B1 >: B] (kv: (A, B1)): scala.collection.Map[A, B1] = {

        super.+[B1](kv)
    }

    abstract override def get(key: A): Option[B] = super.get(key)
    abstract override def iterator: Iterator[(A, B)] = super.iterator
    abstract override def -(key: A): scala.collection.Map[A, B] = super.-(key)
}



var regs = new scala.collection.Map[String, Int] with RegManager[String, Int]

更新: 最后我选择了完成工作的包装器样式实现(装饰器 DP)。我的 Scala 职业生涯只有 6 个月,所以我可能还没有正确理解特征的能力?!不过语言很棒!

4

2 回答 2

1

要么MyTrait[A,B] extends Map[A,B]_MyTrait[A,B] {this: Map[A,B] => ...


抱歉,实际上您不太可能以这种方式进行工作。

我的回答的第一部分与您的 MyTrait 声明没有引用 Map 的事实有关,因此它不可能覆盖 Map 的 +。你需要类似的东西

trait MyTrait[A,B] extends Map[A,B] {
  abstract override def +[B1 >: B](kv: (A, B1)) : Map[A, B1] = {
    println("adding to map") // this is the new part
    super.+(kv) 
   }
}

然而,这不会让你走得太远,原因有很多:

在声明中

var mymap: Map[String, Int] with mytrait[String, Int] = Map[String, Int]()

该值Map[String, Int]()具有类型 Map 并且与您的特征无关。mymap声明类型的变量Map with mytrait不会如此。它只会阻止它编译。

MyTrait 必须出现在地图的创建中,而不仅仅是在变量的类型声明中。那将类似于new Map[A,B] with MyTrait[A,B] (注意new)。

然而,Map是一个特征,而不是一个具体的类,所以你必须引入一些具体的实现。如果您设法做到这一点,那么您必须小心 + 返回的新地图也混合了 MyTrait 类型(当然,您的具体实现不太可能这样做)。否则,一旦您添加了一个元素,您将返回到没有 MyTrait 的地图。

于 2012-08-07T09:50:50.730 回答
0

您不能将特征添加到现有对象。Trait 不能动态添加,只能在创建对象时添加。您可以:

  1. 按照问题评论中的建议,使用pimp your library pattern 。但是,在这种情况下,您不能使用相同+的方法,您需要使用自己的方法名称。
  2. 手动包装您的地图,以便您仍然可以+用作方法名称。
  3. 创建对象的副本并向其添加特征。它可能会导致性能问题,尤其是在原始地图很大的情况下。
于 2012-08-07T10:43:50.317 回答