假设我定义了一个扩展它的特征和案例类:
trait A {
var list: List[Int] = List()
def add(i: Int) = list = i :: list
}
case class B(str: String) extends A
如果我然后创建B的实例,修改列表并复制它,副本将获取特征A中定义的列表值:
val b = B("foo")
b.add(3)
println("B: " + b.list)
println("Copied: " + b.copy(str = "bar").list)
输出:
B:列表(3)
已复制:列表()
我知道这可能不是一个好的函数式编程实践,但有没有办法可以强制B的副本采用列表的修改版本,例如b?
答案 0 :(得分:4)
您可以通过声明案例类中的列表来完成此操作,并通过声明方法来满足您的特征类型。所以:
trait A {
def list: List[Int]
}
case class B(i: Int, var list: List[Int]) extends A
然后你的例子:
scala> val b = B(2, List(1))
b: B = B(2,List(1))
scala> b.list = List(3)
b.list: List[Int] = List(3)
scala> println("B: " + b.list)
B: List(3)
scala> println("Copied: " + b.copy(i = 4).list)
Copied: List(3)
我不知道你在做什么,这需要你班上一个可变的列表,但我想尽管这满足了你的例子,它可能无法满足你从未做过的任何问题。 :
scala> val a: A = b
a: A = B(2,List(3))
scala> a.list
res2: List[Int] = List(3)
scala> a.list = List(4)
<console>:15: error: value list_= is not a member of A
a.list = List(4)
当您尝试将B
的实例视为A
时,您无法分配列表类型。如果您正在处理A的实例,则必须执行类似
scala> a match {
| case itsAB: B => itsAB.list = List(4); itsAB
| case other => other
| }
res3: A = B(2,List(4))
但无论如何,这就是你要做的。或者您可以按照phongnt的建议来创建非案例类并自己定义一个复制方法。
在评论中你说:
我没有提到trait属性需要通过引用特征本身来修改
但这对我来说听起来像全球可变状态。如果你真的需要,我鼓励你不要然后你可以使用一个对象:
object A {
@volatile
var list = List.empty[Int]
}
但是我怀疑这会得到你在你的问题中所表达的内容,因为这里的每个实例都没有单独的列表。所以...看看你更新的问题,我只能猜测这样的事情是你想要完成的事情:
object A {
@volatile
var list = List.empty[Int]
}
trait A {
def list = A.list
def add(i: Int) = A.list = i :: A.list
}
case class B(str: String) extends A
让你:
scala> val b = B("hi")
b: B = B(hi)
scala> b.add(0)
scala> b.add(1)
scala> A.list
res4: List[Int] = List(1, 0)
但同样,这是全球可变的状态,这不是一件好事。它不是线程安全的。因此,如果您没有这样做就可以找出问题...我鼓励您这样做。
答案 1 :(得分:3)
当您声明一个case类时,编译器会为您生成copy
方法,并且它仅使用您在case类中定义的变量进行参数化
因此,您肯定可以通过在案例类中声明变量copy
来为您做list
方法。 case class B(i: Int, list: List[Int])
。您还需要override val
修饰符来满足scalac。但是,scalac不允许您覆盖可变变量,因此您也需要更改特征,例如
trait A {
val list: List[Int] = List(1)
}
case class B(i: Int, override val list: List[Int]) extends A