问题
case class Person(var firstname: String, lastname: String)
val p1 = Person("amit", "shah")
val p2 = p1.copy()
p1.firstname = "raghu"
p1
p2
p1 == p2
As i went through some documentation which says scala copy method of case class uses shallow copy
but output of this example i am not able to crack
i have created copy as person p2 from p1 and then i have changed p1.firstname to "raghu"
so, in case of shallow copy it should change value of p2.firstname but this is not happening here
reference: https://docs.scala-lang.org/tour/case-classes.html
回答1:
Your confusion is about the difference between variables
and values
.
So, when you do something like,
val p1 = Person("amit", "shah")
val p2 = p1.copy()
Then p2
is a shallow copy of p1
, so the variables
p1.firstname
and p2.firstname
point to the same value
of String
type which is "amit"
.
When you are doing p1.firstname = "raghu"
, you are actually telling variable p1.firstname
to point to a different value
of String
type which is "raghu"
. Here you are not changing the value itself but the variable
.
If you were to change to value
itself, then both p1
and p2
will reflect the change. Unfortunately, String
values are immutable in Scala, so you can not modify a String
value.
Let me show you by using something modifiable like a 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))
回答2:
You can imagine the values of String variables, as being references to Strings stored in a Value Store somewhere.
With a shallow copy, all values are still pointed at their original values, there isn't a "second string" created.
However, since the JVM treats string references like values, when firstname is assigned it is now pointing to "raghu"
If we instead wrap the string in another class, let's call it case class Box(var s:String)
Then the JVM (and thus scala) will be using references to orange 'boxes' instead of strings.
case class Person(var firstname: Box, lastname: Box)
val p1 = Person(Box("amit"), Box("shah"))
val p2 = p1.copy()
p1.firstname = Box("raghu")
The same exact graphic applies, because it was a shallow copy.
all the references are copies, and now point to the box, in orange.
if instead of changing the reference to a new box, you change the string inside the box.
p1.firstname.s = "raghu" what you are instead doing is replacing the value inside the box.
If there were some theoretical "deep copy" method.
It would copy the references, the boxes, and the strings inside.
Strings are strange on the JVM. they act like values, and sometimes singleton values, and their reference equality (in java) is messed up because of it, but this is an implementation detail, hidden to both Java and Scala. So we can treat Strings as values. (see 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
来源:https://stackoverflow.com/questions/52966711/scala-case-class-uses-shallow-copy-or-deep-copy