Scala case class uses shallow copy or deep copy?
Asked Answered
R

2

10
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

Restless answered 24/10, 2018 at 10:31 Comment(0)
H
14

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))
Hispaniola answered 24/10, 2018 at 10:48 Comment(0)
E
12

You can imagine the values of String variables, as being references to Strings stored in a Value Store somewhere. enter image description here

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.

enter image description here

If there were some theoretical "deep copy" method. enter image description here

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

Elver answered 24/10, 2018 at 11:3 Comment(1)
Great graphical representation. You may want to change the language of your answer a little bit, sentences such as A String type, is a reference to a String Value are misleading.Hispaniola

© 2022 - 2024 — McMap. All rights reserved.