Абстракция метода 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 ответов
- в чем преимущество метода копирования, созданного компилятором для классов case без каких-либо аргументов? Это просто вернет новый Foo и ничего не скопирует.
- до цитата Лукас 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"))