Case类的copy()方法抽象

时间:2010-05-26 09:16:37

标签: scala copy

我想知道是否可以抽象出案例类的复制方法。基本上我有sealed trait Op之类的东西,然后是case class Push(value: Int) extends Opcase 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()?我希望不会。

5 个答案:

答案 0 :(得分:12)

您似乎将copyclone混为一谈。 copy的目标是制作一个几乎相同的副本,但更改了一些内容。这可能是什么取决于案例类的参数,因此不可能使它成为一种常见的方法。

case class X()的情况下,使用copy方法没有多大意义,因为没有什么可以改变。

另一方面,clone是一种Java方法,其目标是生成对象的完美副本,这似乎是您想要的。

答案 1 :(得分:9)

  1. 编译器生成的复制方法对没有任何参数的案例类有什么好处?这只会返回一个新的Foo,而不是复制任何内容。
  2. quote Lukas Rytz(我相信他已实施):
  3. 只有在类中没有名为“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"))