在父类中创建新实例时保留子类型

时间:2014-05-09 10:54:48

标签: scala

我有以下(简化)类包装Map

class Store(val values: Map[String, String]) {
  def set(name: String, value: String): Store = new Store(values + (name -> value))
  def remove(name: String): Store = new Store(values - name)
}

我想让其他类扩展它来覆盖,比如toString方法:

class PrintableStore(values: Map[String, String]) extends Store(values) {
  override def toString: String = values.toString
}

事后明显的问题是setremove返回Store的实例:这些方法返回的值不是正确的类型,而且我正在失去我的增强型toString方法。

我能找到的唯一解决方案是将Store定义为具有自我类型的特征,如下所示:

trait Store[+Self] {
  this: Self =>

  def values: Map[String, String]
  def copy(values: Map[String, String]): Self

  def set(name: String, value: String): Self = copy(values + (name -> value))
  def remove(name: String): Self = copy(values - name)

}

class PrintableStore(val values: Map[String, String]) extends Store[PrintableStore] {
  override def toString: String = values.toString
  override def copy(values: Map[String, String]): PrintableStore = new PrintableStore(values)
}

这很好用,但需要我认为是Scala代码的惊人数量的样板:

  • 所有子类都将具有基本相同的copy实现。
  • 所有子类都将自己作为参数类型传递给Store

这个问题有更好的解决方案,还是我被宠坏了,我的样板门限太低了? 我必须承认,虽然我觉得我理解自我类型,但我还不太习惯使用它们而且以前的代码可能完全不正确。

1 个答案:

答案 0 :(得分:2)

您可以执行类似于集合库的操作:

trait StoreLike[T <: StoreLike[T]] {
  val values: Map[String, String]
  def set(name: String, value: String): T = build(values + (name -> value))
  def remove(name: String): T = build(values - name)
  def build(values: Map[String, String]): T
}

class Store(val values: Map[String, String]) extends StoreLike[Store] {
  def build(values: Map[String, String]): Store = new Store(values)
}

class PrintableStore(val values: Map[String, String]) extends StoreLike[PrintableStore] {
  override def toString: String = values.toString()
  def build(values: Map[String, String]): PrintableStore = new PrintableStore(values)
}