Подтвердить что ты не робот

Абстракция метода класса экземпляра класса()

Я хотел бы знать, можно ли абстрагировать метод копирования классов 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() для всех классов случаев? Надеюсь, что нет.

4b9b3361

Ответ 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"))