Абстракция метода класса экземпляра класса()
Я хотел бы знать, можно ли абстрагировать метод копирования классов 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)
Есть ли причина, почему компилятор делает это исключение? Я думаю, что это довольно unituitive, и я ожидаю, что каждый класс case определит метод копирования.
Вторая проблема: у меня есть метод def ops: List[Op]
, и я хотел бы скопировать все ops как ops map { _.copy() }
. Как определить метод копирования в значении Op
? Я получаю ошибку "слишком много аргументов", если я говорю def copy(): Op
. Однако, поскольку все методы copy() имеют только необязательные аргументы: почему это неверно? И как мне это сделать правильно? Сделав еще один метод с именем def clone(): Op
и напишите всюду def clone() = copy()
для всех классов случаев? Надеюсь, что нет.
Ответы
Ответ 1
- Какая польза от метода генерирования компилятора для классов case без каких-либо аргументов? Это просто вернет новый Foo и ничего не копирует.
- К quote Lukas Rytz (я считаю, что он его реализовал):
Методы копирования генерируются только в том случае, если в классе нет элемента с именем "copy", который непосредственно определяется или унаследован.
Ответ 2
Кажется, вы сбиваете с толку copy
с помощью clone
. Цель copy
- сделать почти идентичную копию, но с чем-то измененным. Что это может быть связано с параметрами класса case, поэтому невозможно сделать его общепринятым.
В случае case class X()
нет смысла иметь метод copy
, так как там ничего не должно быть изменено.
С другой стороны, clone
- это Java-метод, целью которого является создание совершенных копий объекта, который, кажется, вам нужен.
Ответ 3
Как правильно указал Мирко, вы не можете действительно абстрагироваться от метода копирования. Я поддерживаю мнение Дэниела, что клонирование может быть тем, что вы хотите, хотя я бы обернул его некоторым вспомогательным кодом, чтобы уменьшить шаблон.
Вы можете определить признак 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, вы делаете их расширяющими вспомогательный признак, что делает их более чистыми и сохраняет некоторые нажатия клавиш.
С другой стороны, клонирование не имеет смысла для неизменяемых объектов, поэтому я полагаю, что ваши классы имеют изменяемое состояние. Я бы посоветовал вам пересмотреть, если вы действительно не можете сделать их неизменными и использовать этот тип клонирования только в крайнем случае. Невосприимчивость защитит себя от класса ошибок.
Ответ 4
Зачем вам нужно создавать идентичные копии экземпляров класса 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
Компилятор не создает методы со всеми комбинациями необязательных параметров в определяющем классе. Значения по умолчанию вставляются в том месте, где вызывается метод.
Ответ 5
Упрощенный ответ Бена. Но что, если вы хотите что-то вроде этого:
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"))