Scala case class 使用浅拷贝还是深拷贝?

Scala case class uses shallow copy or deep copy?

case class Person(var firstname: String, lastname: String)

val p1 = Person("amit", "shah")
val p2 = p1.copy()
p1.firstname = "raghu"
p1
p2

p1 == p2

当我浏览一些文档时,其中提到了 scala 复制方法 case class 使用浅拷贝

但是这个例子的输出我无法破解

我已经从 p1 创建了副本作为 person p2 然后我改变了 p1.firstname 至 "raghu"

所以,在浅拷贝的情况下,它应该改变 p2.firstname 的值 但这不会发生在这里

参考:https://docs.scala-lang.org/tour/case-classes.html

你的困惑在于 variablesvalues 之间的区别。

所以,当你做类似的事情时,

val p1 = Person("amit", "shah")
val p2 = p1.copy()

p2p1的浅拷贝,所以variablesp1.firstnamep2.firstname指向同一个value String 类型是 "amit".

当您执行 p1.firstname = "raghu" 时,您实际上是在告诉变量 p1.firstname 指向 String 类型的另一个 value,即 "raghu"。在这里您没有更改值本身,而是 variable.

如果您要更改为 value 本身,那么 p1p2 都会反映更改。不幸的是,String 值在 Scala 中是不可变的,因此您不能修改 String 值。

让我用一些可修改的东西向你展示,比如 ArrayBuffer

scala> import scala.collection.mutable.ArrayBuffer
// import scala.collection.mutable.ArrayBuffer

scala> case class A(s: String, l: ArrayBuffer[Int])
// defined class A

scala> val a1 = A("well", ArrayBuffer(1, 2, 3, 4))
// a1: A = A(well,ArrayBuffer(1, 2, 3, 4))

scala> val a2 = a1.copy()
// a2: A = A(well,ArrayBuffer(1, 2, 3, 4))

// Lets modify the `value` pointed by `a1.l` by removing the element at index 1
scala> a1.l.remove(1)
// res0: Int = 2

// You will see the impact in both a1 and a2.

scala> a1
// res1: A = A(well,ArrayBuffer(1, 3, 4))

scala> a2
//res2: A = A(well,ArrayBuffer(1, 3, 4))

您可以将字符串变量的值想象成对存储在某处的值存储中的字符串的引用。

使用浅拷贝,所有值仍然指向它们的原始值,没有创建 "second string"。

但是,由于 JVM 将字符串引用视为值,因此当分配 firstname 时,它​​现在指向 "raghu"

如果我们改为将字符串包装在另一个 class 中,我们称它为 case class Box(var s:String)

然后 JVM(以及 scala)将使用对橙色 'boxes' 的引用而不是字符串。

案例 class 人(var 名字:Box,姓氏:Box)

val p1 = Person(Box("amit"), Box("shah"))
val p2 = p1.copy()
p1.firstname = Box("raghu")

完全相同的图形适用,因为它是 副本。

所有引用都是副本,现在指向橙色的框。

如果不是更改对新框的引用,而是更改框内的字符串。

p1.firstname.s = "raghu" 你正在做的是替换框中的值。

如果有一些理论上的"deep copy"方法。

它会复制其中的引用、框和字符串。


JVM 上的字符串很奇怪。它们表现得像值,有时是单例值,它们的引用相等性(在 java 中)因此而变得混乱,但这是一个实现细节,对 Java 和 Scala 都是隐藏的。所以我们可以将字符串视为值。 (参见 What is Java String interning? for more on this, but it may be too advanced for now) and a Scala thread: https://www.scala-lang.org/old/node/10049.html