Абстракция метода copy () класса Case

Я хотел бы знать, можно ли абстрагировать метод копирования классов case. В основном у меня есть что-то вроде sealed trait Op а потом что-то вроде case class Push(value: Int) extends Op и case class Pop() extends Op.

первая проблема: класс case без аргументов / членов не определяет метод копирования. Вы можете попробовать это в REPL.

scala> case class Foo()
defined class Foo

scala> Foo().copy()
<console>:8: error: value copy is not a member of Foo
       Foo().copy()
             ^

scala> case class Foo(x: Int)
defined class Foo

scala> Foo(0).copy()
res1: Foo = Foo(0)

есть ли причина, по которой компилятор делает это исключение? Я думаю, что это довольно унитивно, и я ожидал бы каждый случае класс определите метод копирования.

вторая проблема: у меня есть метод def ops: List[Op] и я хотел бы скопировать все ops, как ops map { _.copy() }. Как бы я определил метод copy в Op черта? Я получаю ошибку "слишком много аргументов", если я говорю def copy(): Op. Однако, поскольку все методы copy () имеют только необязательные аргументы: почему это неверно? И как мне это сделать правильно? Создав другой метод с именем def clone(): Op и писать везде def clone() = copy() для всех классов case? Надеюсь, что нет.

5 ответов


  1. в чем преимущество метода копирования, созданного компилятором для классов case без каких-либо аргументов? Это просто вернет новый Foo и ничего не скопирует.
  2. до цитата Лукас Rytz (Я считаю, что он реализовал его):
методы копирования создаются только в том случае, если в классе нет члена с именем"copy", непосредственно определенного или унаследованного.

Вы, кажется, путаете copy с clone. Цель copy с почти идентичная копия, но что-то изменилось. Что это может быть, зависит от параметров класса case, поэтому невозможно сделать его общим методом.

в случае case class X(), это не имеет большого смысла иметь copy метод, так как там нечего менять.

С другой стороны, clone - Это метод Java, целью которого является произведите идеальные копии объекта, который кажется, что вы хотите.


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

вы можете определить признак mixin с функциональностью копирования и просто смешать его с классами case:

trait ClonableAs[T] extends Cloneable { this: T => 
  def makeClone() = super.clone().asInstanceOf[T]
}

case class Foo(i: Int) extends ClonableAs[Foo]

List(Foo(1), Foo(2), Foo(3)).map(_.makeClone())

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


зачем создавать идентичные копии экземпляров класса case? Классы Case по умолчанию неизменяемы, поэтому их можно безопасно использовать.

в любом случае, я не думаю, что вы можете сделать то, что вы просите с параметрами по умолчанию:

scala> trait Op { def copy():Op }          
defined trait Op

scala> case class Op1(v:Int) extends Op    
<console>:6: error: class Op1 needs to be abstract, since method copy in trait Op of type ()Op is not defined
       case class Op1(v:Int) extends Op

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


перевел ответ Бена. Но что, если вы хотели что-то вроде этого:

sealed trait Op 
case class Push(value: Int, context:String) extends Op
case class Pop(context:String) extends Op

val stackOps = List(Push(3, "foo"), Pop("foo"))

def copyToContext(newContext:String, ops:List[Op]): List[Op] = {
    // ... ?
}

val changedOps = copyToContext("bar", stackOps)

// would return: List(Push(3, "bar"), Pop("bar"))