我希望我的类既可以是不可变的又可以更新的。这是我的样板:
case class A(a: Int, b: Int) {
def withB(newB: Int) = copy(b=newB)
}
它按预期工作。问题是,如果我希望客户端派生A
但不直接实例化它会怎么样?这样:
abstract case class A(a: Int, b: Int) {
def withB(newB: Int) = copy(b=newB)
}
不起作用。 (当然不是,如何在没有实例化的情况下复制自己?)
答案 0 :(得分:1)
您需要开始查看其他模式以帮助您更新不可变对象。开始查看Lens
类型。他们的工作方式如下:
trait Lens[A,B]{
self =>
def get(obj: A): B
def set(obj: A, value: B): A
def andThen(that: Lens[B, C]): Lens[A,C]{
def get(obj: A) = that get (self get obj)
def set(obj: A, value: C) = self set (obj, that set(self get obj, value))
}
}
这样你就可以定义一个“get”和“set”,它既与纯粹(它们通过制作副本一起工作)和Scala中大多数事物的可组合性质保持一致。因此,在您的示例中,您不会在对象本身上定义withTwo
方法,而是为其创建Lens
。
使用镜头:
case class Bar(c: Int)
case class Foo(a: Int, b: Bar)
object LensFooBar extends Lens[Foo, Bar]{
def get(foo: Foo) = foo.b
def set(foo: Foo, bar: Bar) = foo.copy(b = bar)
}
object LensBarC extends Lens[Bar, Int]{
def get(bar: Bar) = bar.c
def set(bar: Bar, value: Int) = bar.copy(c = value)
}
val myFirst = Foo(1, Bar(2))
val updated = LensFooBar.set(myFirst, Bar(3))
val lenser = LensFooBar andThen LensBarC
val updatedFurther = lenser set(myFirst, 3)
这两个镜头都在做同样的事情,但这不仅应该说明如何使用不可变的嵌套结构,还应该说明如何使用不可变对象,而不是为要在类中更改的每个字段放置一个方法。
您可以看到此Scala Days 2013 talk或了解Scalaz如何实施镜头here。
答案 1 :(得分:0)
如果我希望客户端派生
A
但不直接实例化它呢?
您可以使用Builder模式执行此操作。您的构建器将是一个(case)类,其中包含创建A
所需的所有内容,您可以将其传递到需要实例化A
的位置。
例如,
case class ABuilder(one: Int = 1, two: String = "") {
def withOne(v: Int) = copy ...
def withTwo(v: String) = copy ...
def build(): A = ...
}