Scala: Как я могу сделать мои неизменные классы более легкими для подкласса?

Недавно я создал неизменяемый класс, поддерживающий такие операции, как +, - и т.д., который возвращает новый экземпляр этого класса при его изменении.

Я хотел сделать подкласс этого класса, чтобы добавить немного состояния и функциональности, но теперь у меня возникает проблема в том, что все исходные методы класса возвращают экземпляры себя, а не подкласс.

Основываясь на моем текущем ограниченном знании Scala, я могу придумать следующее:

class Foo(val bar:Int) { 
  def copy(newBar:Int) = new Foo(newBar)
  def + (other:Foo):This = copy(this.bar + other.bar) 
}
class Subclass(barbar:Int) extends Foo(barbar) { 
  override def copy(newBar:Int) = new Subclass(newBar)
  override def + (other:Subclass) = super.+(other).asInstanceOf[Subclass]
}

Проблема здесь совершенно очевидна - все операции суперкласса, которые возвращают новый экземпляр, должны быть переопределены в подклассе с литой.

Сначала "this.type" казался многообещающим, но "this.type" включает только "this", а не какой-либо другой объект того же типа.

Существует ли стандартный шаблон для упрощения подстановки классов неизменяемых классов? Что-то вроде:

class Foo(val bar:Int) { 
  def copy(newBar:Int):SameType = new Foo(newBar)
  def + (other:Foo) = copy(this.bar + other.bar) 
}
class Subclass(barbar:Int) extends Foo(barbar) { 
  override def copy(newBar:Int):SameType = new Subclass(newBar)
  override def + (other:Subclass) = super.+(other).asInstanceOf[Subclass]
}

Этот конкретный подход потребует, чтобы компилятор требовал, чтобы все подклассы реализовали метод copy(), который возвращает тот же тип, что и этот подкласс, что со мной было бы прекрасно. Тем не менее, я не думаю, что в настоящее время в Scala ничего подобного не существует.

Некоторые соображения, которые приходят на ум, следующие:

  • Использовать делегирование - но, конечно же, я все еще буду переустанавливать все методы как вызовы делегатов.
  • Используйте неявные типы для добавления операций вместо подкласса
  • Используйте изменяемую структуру данных. Это, вероятно, самое простое и быстрое решение, но я потеряю преимущества использования неизменяемых структур данных (о которых я все еще надеюсь узнать больше).

Я уверен, что это уже много раз обсуждалось, и я прошу прощения за повторный запрос. Я сделал Google для дублирования вопроса без успеха, поэтому мои поисковые запросы, должно быть, были плохо сконструированы.

Спасибо заранее,

Dobeš

Ответы

Ответ 1

Вы можете использовать черту реализации, как и классы коллекции, которая параметризована конкретным типом. Например, что-то вроде:

trait FooLike[+A] {
  protected def bar: Int

  protected def copy(newBar: Int): A
  def +(other: Foo): A = copy(bar + other.bar)
}

class Foo(val bar: Int) extends FooLike[Foo] {
  protected def copy(newBar: Int): Foo = new Foo(newBar)
}

class Subclass(barbar: Int) extends Foo(barbar) with FooLike[Subclass] {
  protected def copy(newBar: Int): Subclass = new Subclass(newBar)
}