我想知道是否可以抽象出案例类的复制方法。基本上我有sealed trait Op
之类的东西,然后是case class Push(value: Int) extends Op
和case class Pop() extends Op
之类的东西。
第一个问题:没有参数/成员的案例类没有定义复制方法。你可以在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 map { _.copy() }
。如何在Op
特征中定义复制方法?如果我说def copy(): Op
,我会收到“太多参数”错误。但是,由于所有copy()方法都只有可选参数:为什么这不正确?而且,我该怎么做呢?通过创建另一个名为def clone(): Op
的方法并为所有案例类写入def clone() = copy()
?我希望不会。
答案 0 :(得分:12)
您似乎将copy
与clone
混为一谈。 copy
的目标是制作一个几乎相同的副本,但更改了一些内容。这可能是什么取决于案例类的参数,因此不可能使它成为一种常见的方法。
在case class X()
的情况下,使用copy
方法没有多大意义,因为没有什么可以改变。
另一方面,clone
是一种Java方法,其目标是生成对象的完美副本,这似乎是您想要的。
答案 1 :(得分:9)
只有在类中没有名为“copy”的成员,直接定义或继承时,才会生成复制方法。
答案 2 :(得分:2)
正如Mirko正确指出的那样,你无法真正抽象出复制方法。我支持Daniel的观点,克隆可能就是你想要的,虽然我会用一些辅助代码包装它以减少样板。
您可以使用复制功能定义mixin特征,然后将其混合到您的案例类中:
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类添加一个相同的方法,你可以让它们扩展helper trait,这样可以使它们变得更干净,并为你节省一些键击。
另一方面,克隆对于不可变对象没有任何意义,因此我推断你的类具有可变状态。我建议你重新考虑一下你是否真的不能让它们变成不可变的,并且只能在最后使用这种类型的克隆。不变性将保护自己免受一类错误的影响。
答案 3 :(得分:1)
为什么需要为案例类实例创建相同的副本?默认情况下,案例类是不可变的,因此可以安全地共享。
在任何情况下,我认为你不能用默认参数做你想要的事情:
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
编译器不会在定义类中创建包含可选参数的所有组合的方法。默认值将插入调用方法的位置。
答案 4 :(得分:1)
Upvoted Ben的回答。但是,如果你想要这样的东西怎么办:
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"))